Bash计算方差

一个计算方差的bash函数,使用bc进行计算,默认精度是20(bc-l选项)
Continue reading “Bash计算方差” »


随机日志:
sudo与su(原创翻译)

Original:http://fosswire.com/2008/02/03/sudo-vs-su/

Original written by Jacob

可能已经见过两个命令一个。Ubuntu用户sudo更加熟悉一点与此同时SUSE,Fedora和Debian用户更加常用su命令到底两个命令区别什么

两个命令允许得到root权限两者区别在于如何获得这个权限 Continue reading “<br/><small><i>随机日志:</i></small><br /> sudo与su(原创翻译)” »


随机日志:
通过googleearth-package生成Google Earth的deb安装包

今天同学聊天提到Google Earth突然一个玩玩于是网上找到google的APT里面安装添加然后update直接输入sudo aptitude install google然后tab安装一个叫googleearth-package软件包以为这个就是google的APT里面下载的Google Earth但是安装菜单到Google Earth选项

新立得搜索这个软件包发现这个软件包作用从Google Earth的bin安装创建deb不知道为什么,Google Desktop和Picasa的Linux官方的deb就Google Earth没有难怪安装这个软件包把fakeroot安装……
Continue reading “<br/><small><i>随机日志:</i></small><br /> 通过googleearth-package生成Google Earth的deb安装包” »

抛砖:使用脚本添加、删除挂载的思路

写这个日志的主要目的是想抛砖引玉得到更好的解决方案,为了准确描述我的需求只好废话比较多地讲一下具体的需求了:-)

最近有一个需求,需要限制某个用于察看线上日志的log用户的权限,要让权限要尽可能小但是又能看到日志文件。这样的需求很明显需要用chroot来完成。但是细想起来有一个日志目录访问问题。

假设目录所在文件夹是/var/logs,为了保证log用户能访问到这个文件夹,至少需要chroot环境中包含这个目录。如果直接这么做,log用户的chroot环境就与日志所在文件夹混在一起,不方便维护。我想到的解决方案是使用mount将目录bind到另一个文件夹下,然后在这个文件夹上级目录创建chroot环境。

例如:log用户的chroot环境处于/home/log中,那么使用mount --bind /var/logs /home/log/home/log。这样就可以让log用户进入chroot环境后看到日志,而又可以分开维护chroot文件夹和日志文件夹。

继续顺着这个思路往下走,就遇到了这篇日志的主要问题:如何使用脚本添加、删除挂载。

最终目的是尽可能自动化地进行chroot环境的创建及删除,所以同样地也要做到挂载的自动创建及删除。在挂载方面需要自动化完成以下目的:

  1. 执行创建chroot脚本后,挂载相应目录并将挂载信息写入fstab
  2. 执行清理chroot脚本后,卸载对应目录并删掉fstab中的挂载信息

第一条相对简单,在创建chroot脚本里直接写相应的mount语句并加入echo XXX >>/etc/fstab就可以。

第二条是我遇到的难点。如何识别“对应目录”以及如何识别fstab里对应的挂载信息?这个过程非常重要,必须要保证清理干净。实际情况是我还需要将/dev绑定到chroot环境中,如果不清理干净,其他人可能会直接rm删除chroot环境,导致/dev路径下的文件被删除。

我目前的解决方案:

创建挂载点:

  • echo "/var/logs /home/log/home/log/logs bind defaults,bind 0 0 #CHROOT_MOUNT_BIND_LOG" >>/etc/fstab
  • mount -a

清理挂载点

  • 识别老的挂载点:MOUNT_POINT=`grep CHROOT_MOUNT_BIND_LOG /etc/fstab|awk '{print $2}'`
  • 去掉卸载:umount ${MOUNT_POINT}
  • 删掉fstab记录:sed -i '/CHROOT_MOUNT_BIND_LOG/d' /etc/fstab

这个方案个人感觉有点糟糕,不是很精确。希望大家能提供更好的思路:-)

Bash里使用getopts解析非选项参数

getopts是一个Bash built-in,可以用它来实现与getopt(3)一致的参数解析功能。

注意它和getopt(1)的区别。getopt是一个单独的命令,而getopts是bash内置命令。

关于option与argument的区别,我这里实在给不出准确的定义,只能根据自己的理解试着解释一下。

argument是通常所说的命令行参数,在C语言里就是argv数组,根据参数出现的顺序从argv[1]开始依次递增(argv[0]里是被执行的程序本身的程序名)。在Bash里,$0表示脚本的名称,$1开始往后是各个argument。

option有点混乱。我的理解是:一个Option是以-(hyphen-minus character)或--开头的字符串,它后面有可选的argument,如果有则只有一个。也就是说,一个option由一个或两个argument组成

根据上面的描述命令行参数(Arguments)可以分为3类(抱歉我这里会用比较山寨的方法来描述这三类,如果有对应的标准名称,请留言指出)

  1. Option with an argument:例如wget的-O选项,必须要指定一个路径或文件名作为参数
  2. Option without an argument:看到网上有的地方也称为switcher,开关选项。例如wget的-v和-q选项,可以使程序输出信息为verbose(详尽)或quiet(安静)
  3. Non-option argument:其实根据上面的描述,这个第三类有点尴尬:它不符合我定义的Option。例子就是wget命令指定文件的URL,它的前面并没有对应的-开头的字符串。

getopts的基本用法类似下面的例子:

while getopts :s:h opt
do  
    case $opt in
        s)  
            echo "-s=$OPTARG"
            ;;
        :)
            echo "-$OPTARG needs an argument"
            ;;                                                                                 
        h)  
            echo "-h is set"
            ;;
        *)  
            echo "-$opt not recognized"
            ;;
    esac
done

这个例子中,定义了一个optstring(:s:h)。其中第一个:字符,打开了getopts的silent模式,可以自行对未识别的选项进行处理而不是让getopts自己报错。随后的s:定义了一个带参数的选项,如果-s使用时没有带上对应的argument则可以在随后的case语句中做相应的报错处理。最后的h定义了一个不带参数的选项。getopts的使用基本就是这样了,更详尽的getopts的optstring的定义和使用请参考bash(1)

上面的例子里我没有给出如何解析非选项参数(Non-option argument)。如果我执行./test.sh -s test_string -h filename,那么仅靠getopts是无法获取到filename这个字符串的。

仔细阅读bash(1)之后就会发现,getopts在访问到第一个non-option argument的时候即会停止解析,并设置$OPTIND为第一个non-option argument在$@中的指针。例如上面这个./test.sh -s test_string -h filename例子,getopts解析完之后会将$OPTIND设置为4。

结合$OPTIND变量和shift这个bash built-in,可以写出一个完整的解析所有参数的脚本。以下是一个例子,已经有恰当的注释信息,应该不需要额外解释了。:

#!/bin/bash
 
declare -a NOA # Array used to store non-option argument
 
while [ $# -ne 0 ]
do
    # if OPTIND > $#+1, getopts will not change OPTIND's value,so set it to 0
    OPTIND=0
    while getopts :s: opt
    do
        case $opt in
            s)
            echo "-s=$OPTARG"
            ;;
            \?)
            echo "-$OPTARG not recognized"
            ;;
        esac
    done
 
    # if getopts find a non-option argument
    # it will stop parsing and return OPTIND
    # as index to the first non-option argument
    # 
    # if getopts doesn't find any non-option argument
    # it will set OPTIND=$#+1
    if [ $OPTIND -ne $(($#+1)) ]
    then
        shift $(($OPTIND-1))
        # This is just my favorate way to append element to an array in shell.
        # Feel free to change it
        NOA=(`echo ${NOA[*]}` $1)
        shift
    else
        break; # getopts doesn't find any non-option argument
    fi
done
 
echo ${NOA[*]}

Partially blocked by GFW

yegle.net及其子域名被DNS污染,发生时间北京时间2011年2月20日14时30分左右

184.105.128.93 yegle.net

Congrats for your being listed as “a good service provider”, certified.

R.I.P. yegle.net & *.yegle.net

Mac字幕自动下载器

射手影音最近做了一件很有争议的事情:将SPlayerX摆上了Mac AppStore。SPlayerX是一个Fork自MPleryX的影音播放器。这次射手为了完全规避GPL协议的限制,将字幕下载部分功能用二进制可执行程序方式释出,以fork运行的方式调用这个名为sscl的二进制文件下载字幕。

姑且不论其将二进制文件与GPL协议软件捆绑发行是否违反GPL协议以及GPL协议软件与Mac AppStore的协议是否有冲突。射手这次的做法恰好为所有Mac用户送上了sscl这个大礼,可以使用这个工具自动匹配本地硬盘的视频文件。

原来的设想是用AppleScript写一个脚本,并在Finder里自动监控特定目录,这样每当某目录下有新视频文件添加时就会自动下载字幕。但是我对AppleScript实在不熟悉,加上相关参考资料太少,遂作罢用bash写了一个脚本。如果有AppleScript高手,请改写之:-)

果然抛砖引玉,请参考利用Hazel让Mac自动下载电影字幕更加自动化进行字幕下载操作:-)

sscl的字幕下载目的目录是固定的,所以脚本里就直接Hardcode进去了。


注意:
射手影音并没有为sscl单独设置一个license,我只能根据SPlayerX本身免费下载认为它是freeware。提取该文件单独运行引起的版权上的争议不在本文讨论范围内。
本文中提到的find、mktemp为GNU findutils和GNU coreutils里的版本,请通过MacPort或其他Port系统安装之后再执行

功能描述:
执行脚本时,在预设的目录内查找mkv和avi文件,过滤掉某些0day release里的sample文件。
对于每个文件,如果不存在对应的.chn.srt字幕文件,就调用sscl进行下载并复制到电影文件所在文件夹。

使用方法:
1、下载sscl文件。该文件可以从SPlayer Mercurial源里获取,地址是:http://hg.splayer.org/splayerx/raw/0b9e84441210/binaries/x86_64/sscl
2、修改该文件权限,终端下chmod +x sscl
3、将以下代码保存为sub.sh。请根据你系统内的bash路径修改shabang,我的系统bash是在/Users/yegle/Gentoo/bin/bash的,一般的mac系统应该是在/bin/bash。同时还要修改SSCL为你系统内sscl二进制文件路径,以及MOVIE_DIR你的电影文件所在路径。
4、在终端下执行sh sub.sh,即可自动匹配你的MOVIE_DIR目录下所有电影文件并自动将字幕复制到你的电影文件所在文件夹。如果有字幕没有下载到,将会在命令行里提示某个视频没有找到对应字幕。

#!/Users/yegle/Gentoo/bin/bash
 
MOVIE_DIR=/Users/yegle/Downloads/
MOVIE_LIST=`mktemp`
SSCL=/Users/yegle/sscl
 
rm -rf "$HOME/Library/Application Support/SPlayerX/SVPSub/"*
find $MOVIE_DIR -regextype posix-extended -iregex '.*\.(mkv|avi)$'|grep -iv sample >$MOVIE_LIST
 
while read movie_file_path
do
    movie_filename=`basename "$movie_file_path"`
    movie_path=`dirname "$movie_file_path"`
    if [ ! -e "${movie_file_path%.*}.chn.srt" ]
    then
        $SSCL --video-file "$movie_file_path" --pull >/dev/null 2>&1
        ls "$HOME/Library/Application Support/SPlayerX/SVPSub/${movie_filename%.*}.chn.srt" >/dev/null 2>&1 \
            && mv "$HOME/Library/Application Support/SPlayerX/SVPSub/${movie_filename%.*}".*.srt "$movie_path" \
            || echo $movie_file_path subtitle not found
    fi
done < $MOVIE_LIST

免翻墙dabr

自从twitter废弃basic auth之后,dabr的登录就一直是个头疼的事情。虽然官方提供了数据库存储oauth access token的方案,但是这样还需要多个数据库支持…麻烦…

大家也知道,twip4是有一个模拟OAuth登录的功能的,也就是说输入用户名密码完成oauth认证。这个模拟OAuth功能是@tifan同学完成的(大家鼓掌!),稍加修改后这个oauth_proxy.php还可以通用化。以下就是将oauth_proxy.php使用于dabr实现免翻墙登录的方法。


警告:不要在任何非自己搭建的dabr上输入密码!不要在不支持https的dabr上输入自己的用户名密码!(理论上说,没有使用https,GFW可以抓到你的twitter用户名密码)

以下操作假设你已经有一个搭建好的可用的dabr站点,如果没有,请先参考官方wiki搭建好。假设这个站点是 http://example.com/dabr/

下载文件

免翻墙dabr需要从twip项目里取到以下文件:oauth_proxy.php、simple_html_dom.php
下载路径是

http://twip.googlecode.com/svn/trunk/oauth_proxy.php
http://twip.googlecode.com/svn/trunk/include/simple_html_dom.php

将这两个文件放在dabr的根目录里,也就是

http://example.com/dabr/oauth_proxy.php
http://example.com/dabr/simple_html_dom.php

修改文件

首先要修改oauth_proxy.php里,include simple_html_dom.php的路径。将oauth_proxy.php里,开头的

include('include/simple_html_dom.php');

修改为

include('simple_html_dom.php');

然后修改common/user.php文件。找到 function user_oauth(),该函数的最后一部分是:

        // redirect user to authorisation URL
        $authorise_url = 'http://api.twitter.com/oauth/authorize?oauth_token='.$token['oauth_token'];
        header("Location: $authorise_url");

修改为:

        // redirect user to authorisation URL
        $authorise_url = 'http://api.twitter.com/oauth/authorize?oauth_token='.$token['oauth_token'];
        if($_POST){
            header('Location: ' . BASE_URL.'oauth_proxy.php?p='.base64_encode($_POST['password']).'&u='.base64_encode($_POST['username']).'&g='.urlencode($authorise_url));                                                                                                                                 
        }
        else{
            header("Location: $authorise_url");
        }

最后修改登录页面的html,加入登陆表单html。仍然是common/user.php,找到function theme_login(),该函数里开头一部分是:

$content = 'blablablabla';

在该行下方添加一行:

$content .='                                                                                 
<p>Try the new oauth-simulate login</p>
<form action="'.BASE_URL.'oauth" method="post">
<p>Username: <input type="text" name="username"/></p>
<p>Password: <input type="password" name="password"/></p>
<p><input type="submit" value="Login"/></p>
</form>';

如果你愿意改呢,还可以继续在这个基础上改得漂亮一点,反正以我的审美…凑合了…

现在返回dabr首页,你将看到一个用户名密码输入框,输入你的twitter用户名密码后即可完成登录。

autoproxy2pac代理

autoproxy2pac.appspot.com经常由于over quota无法使用,搭建了一个代理方便大家使用

代理地址

https://yegle.net/autoproxy2pac/

使用方法

去 https://autoproxy2pac.appspot.com 获取与你的代理服务器相对应的pac文件URL

例如你的pac文件URL是 http://autoproxy2pac.appspot.com/pac/proxy/10.13.0.1/3128,那么你可以使用 https://yegle.net/autoproxy2pac/proxy/10.13.0.1/3128 作为你的pac文件URL

如果是自定义的pac文件URL,例如 http://autoproxy2pac.appspot.com/pac/u/example,那么你可以使用 https://yegle.net/autoproxy2pac/u/example 作为你的pac文件URL。以此类推

注意,必须要用https访问

每个文件会缓存半个小时,超过之后会在下一次请求文件时重新缓存

如果在重新缓存时恰好autoproxy2pac.appspot.com处于over quota状态,则继续使用原缓存文件,下次请求文件时继续尝试重新缓存,保证在autoproxy2pac网站本身不可访问时pac文件能正常使用

在Mac OSX上安装gentoo-prefix

不想看废话的同学们可以直接跳转到安装步骤

什么是Ports系统?

众所周知Mac OSX是一个UNIX操作系统。UNIX操作系统的认证是Open Group给的,只有经过他们认证之后才能自称自己是UNIX操作系统并使用UNIX的商标(毕竟人家这是注册商标,不能随便用)。

得益于UNIX标准的建立,越来越多在Linux上开发的开源软件已经可以直接在UNIX下编译安装而不需要考虑跨平台问题,这意味着在Mac OSX上也可以享受绝大多数Linux下开源软件的最新版本。

注意到,我这里提到的是“编译安装”。即使你是一个对编译安装感觉很平常的Linuxer,在Mac下编译安装也不是一件简单的事情,因为你需要自行解决一大堆依赖。有多复杂,你可以尝试安装一次LFS。虽然Linux下常见的包管理系统Mac下没有,但是Mac上有相对于包管理系统来说更加强大的Ports系统(IMO),让这些软件包的安装过程自动化了。

Ports系统的历史还真不好找,从wiki获得的信息是,这是早期BSD系统就开始有的一套比较“原始”的包管理系统:自动从官方网站下载源码包、解压、打补丁、编译安装,并维护各个软件包之间的依赖关系。使用Ports系统,可以通过自动化方式从源码安装软件,而无需考虑其背后的繁琐操作。

除了BSD系统上的Ports系统之外,Linux下常见的Ports系统有:Gentoo Portage和ArchLinux ABS。在Mac OSX上也有多个Ports系统可以选择,比较有名的有MacPortsHomeBrewSorry,还有一个什么Brew的忘记名字了…感谢blackgigi提醒:-))。Gentoo-prefix反而是一个不太常用的Mac下的Ports系统。
Continue reading “在Mac OSX上安装gentoo-prefix” »

多大个事?-记人人网泄密事件

有一种逻辑很可怕。在做一些事情的时候,总有人跳出来问:这多大个事?至于吗?

就像当年举报射手播放器没有遵守GPL,就像人肉某知名“女”推友doublechou其实是个男的。这样的事情,有人总会发问:多大个事?至于吗?你这是什么目的?你怎么又拿出来说事?

为什么又拿出来说事?因为事情根本没有正面解决,不了了之。同样,这次人人网泄露用户资料事件,目前也处于不了了之的状态,我不希望这件事情不了了之,在这里回放一遍完整版本。

我这人习惯不好,Gmail里的spam邮件也会一个一个去看标题。9月3日打开邮箱spam的时候发现一封标题为《仅98元!畅想价值1088元一线明星御用摄影艺术总监萧瑶的全程拍摄服务,让你成为第二个性感小S!仅此一次,不容错过!》的垃圾邮件邮件。邮件的抬头是”请使用真实姓名“。

这个名字很特殊,第一时间让我联想起我某个被人人网封禁的帐号。这个帐号在封禁14天解封后,名字被管理员由原来的yegle修改为”请使用真实姓名“了。至于为什么被封禁,原因很有趣,需要另起一段。

我自己写了个脚本,用于将twitter发的推自动同步到人人网。众所周知twitter上发的推很多是敏感话题,时不时就会被管理员删除掉,同时会发一封站内信到你的人人帐号,内容为”你的状态XXX由于不符合XXXX已被管理员删除“。某一天我心血来潮,又写了个脚本,自动将人人网收件箱里收到的发自管理员的”你的状态XXX由于不符合XXXX已被管理员删除“的站内信自动又发送到人人。于是在某一个下午,某个兢兢业业的人人网客服把我的某条状态删除、重发、删除,来回折腾几次之后一怒之下把我帐号封禁了,就是这样的。

好的扯远了。当时看到这个邮件和抬头之后,第一时间联想到的是人人网,并且没有联想到其他网站。因为全互联网,能把我的邮箱地址和”请使用真实姓名“联系起来的只有人人网上。于是我发了一条tweet

http://img.ly/20RE 人人网出售用户资料的证据:我的某个人人网账号被封禁之后被管理员修改名字为“请使用真实姓名”,今天我收到了这样一封垃圾邮件。

当时只是很随意地发出来了,因为在我眼里,人人网出售用户资料简直是必然事件,这次只不过是抓了个现行,没什么可深究的。但是后来各位推友热情的转发,让东莞时报注意到了,发了一条报道。再加上@xjp同学写的日志推波助澜,终于让这件事情最大化了。

TechWeb、速途等等唯恐天下不乱、五十步笑百步的网站终于正大光明地出来五十步笑百步了,分别报道了这个事情。从那之后互联网上其他的关于此次事件的报道基本是这两篇本来就不是非常客观的报道的再次演义,包括QQ科技、网易等等网站上面的报道

TechWeb:人人网否认出售用户资料称正通过法律手段处理
速途:人人网涉嫌出售用户资料 被疑与团购网站利益互换

其中TechWeb的报道是在2010.09.07 12:47发布的。此报道中,千橡互动集团公关部总监王毅声称,”人人网正在积极和报道中涉及的网友“一阁”联系,且已经联系上,并在帮他处理。“但是事实上,我当时特地将所有IM工具、所有邮箱、所有人人网马甲的站内信都翻了一遍,没有任何联系我的记录。于是我又在twitter上发了一条tweet感慨道:

原来这就是传说中的被联系啊

最后证明,原来是那篇报道的作者穿越到2个小时之后采访的王毅。因为我是在当天下午3点钟接到了人人网的电话(tweet记录

客服的问答我也在twitter上播报了。相关内容如下:

tweet: 人人网客服对话:1、问我有没有用外挂,答:我不用windows
tweet: 人人网客服对话:2、问我有没有用SNS同步软件,答:我自己写脚本同步的
tweet: 人人网客服对话:3、问我有没有点各种应用的链接,答:没有
tweet: 人人王客服问答:4、我提问:那你既然在调查隐私泄露问题,请问能否解释一下我的手机号码你是怎么获得的吗?答:这个是领导给的,我是客服不知道领导的做事方式(大致意思,非原话)
tweet: 人人网客服问答:5、大意是希望我不要再发表某些言论,答:我确保我没有说错

关于手机号码,很多人有疑问,是不是我自己不小心泄露了。第一我没有在人人网上填写手机号码,第二我的域名whois信息里的手机号码早已弃用。人人网如何获取到我的手机号码,我不想去猜测了,丫估计也不会给我答复。很遗憾,在那之后人人网再也没有联系过我,估计是准备把这个事件冷处理掉了。

感谢@xjp同学的跟进,陆续收到其他推友的反馈,也收到了类似或者一样的垃圾邮件,都与人人帐号相关。列举如下:

tweet: 9月3号的时候我也收到了一个团购网站的邮件,里面直接写明了我的真名,而且是繁体的,综合@yegle的事件,我只有在人人网中使用的是繁体书写的中文名
tweet: 翻垃圾邮件箱才发现9月3日我也收到了一个团购网站的邮件,用的是我在人人网去年64时违规被注销帐号的假名。截图如下。 http://twitpic.com/2m37ai cc @yegle

本次事件回放完毕。

廉价OTP解决方案:yubikey

可到http://yubikey.taobao.com/购买

@ohsc那里了解到yubikey这个东西。作为信息安全的学生,我的第一反应是:两眼发亮。为什么两眼发亮呢?慢慢解释。

首先要从什么是OTP说起。
OTP是One-Time Password的简称,中文对应的准确翻译应该叫“一次一密”。根据香农(这个人…信息论、现代密码学,以及其他乱七八糟东西的发明者,牛逼至极的人物啊!)创立的现代密码学理论,一次一密是不可能被破解的,除此之外所有的其他加密方案都至少能被暴力破解,虽然破解的难度是指数级的。(当然,香农说的一次一密不可破解是有前提条件的,具体参考维基百科上的”一次性密码本“词条)。具体来说,一次一密就是在每次加密会话过程中,对传送的明文使用不同的密钥进行加密,每次使用的密钥是随机、不可预测、不重复使用的,从而保证了加密是绝对不可破解的。
当然,我说的是简单化的一次一密的实现,实际操作过程中,密钥的长度必须大于等于明文长度,而且密钥必须是可验证的(所以也可以看成是可预测的)。真正的一次一密,等于需要一个安全信道来传送不少于明文长度的密钥,几乎是没有意义的。

其实生活中已经有很多一次一密的例子,例如中国银行的E-TOKEN、魔兽世界安全令牌、中国建设银行的动态口令卡(纸制),这些都是一次一密的。

国内的一次一密电子令牌基本上使用的是SecurID,EMC公司下属的RSA公司出品的。这种一次一密解决方案使用一个预先定义的time-based的函数计算密码,称为Time-synchronized one-time password。每个token出厂前与服务器同步时间,在出厂后,token和服务器以相同的时钟计算,每分钟变化一次。

在验证token上读取的数字时,服务端可以设置允许的误差值,例如允许前后5分钟内产生的密钥输入都认为是有效的,这样可以最大限度避免token的时钟漂移。

经仔细查证后,上文描述有误。准确的实现是:RSA的服务端验证时允许前后30秒或前后60秒内的密钥为有效密钥。服务端会保留当前token,前一token,后一token三个token值,如果用户输入与当前token一致则不做变化。如果用户输入的是前一token或后一token,服务器将记录这个SecurID的ID对应的offset,下次验证时考量这个offset。如果用户超过了这三个token的值范围,用户将被提示输入SecurID显示的下一个token,如果与上一个输入的token顺序一致,系统将判断验证通过,并记录这个较大的offset值。

这样的方案安全性很高,很多公司内部的vpn系统、线上服务器登陆系统等等都使用了SecurID进行认证。缺点很明显:价格高,据说每个的成本在¥200(网易出的那个山寨版我就不评论了…售价居然才60左右…只能说不太靠谱了…),而且需要配套RSA的服务端方案;需要电池保证时钟运作,因此存在更换token的情况。

另一种完全不同的一次一密方案是使用数学方法生成密钥序列,称为Mathematical-algorithm-based one-time password。这种算法使用数学函数生成密钥序列,然后依次使用密钥序列,从而不需要考虑时间因素。由于密钥序列生成过程中没有时间因素,实际生产的硬件token可以不包含电池供电,免去服务端与token的对时,成本可以下降,同时也没有电池耗尽更换token的担忧(更换token=安全隐患,因为需要人的参与,人永远是任何安全体系的最大漏洞…)。维基百科上举例了一个使用hash chain方法产生此类OTP的方案:找到一个单向函数f(例如任意一个hash函数),给定一个初始种子s。根据f和s产生序列:f(s), f(f(s)), f(f(f(s)))…。然后将该序列倒置,依次作为密钥使用。第三方如果偶然获取到序列中某个密钥,他就必须要通过f的反函数来计算下一个密钥,而f的单向性保证了这样的计算是不可行的。

yubikey使用的是第二种方案。它是一个廉价的OTP解决方案,10枚yubikey的平均价格是$28,由yubico提供验证服务器并提供API供开发者调用。它不需要电池供电,也没有液晶屏显示密钥。在插入电脑之后,它会被识别为一个USB键盘,在任意输入框聚焦并按一下yubikey上的硬件按钮,yubikey将自动模拟键盘输入一串一次性密钥。由于yubikey在电脑上被识别为USB键盘,它可以做到最好的免驱动支持(现在没有操作系统不支持USB键盘了吧?),可以在Windows/MacOSX/Linux上使用。关于yubikey使用的OTP生成方案,强烈建议阅读一下官方提供的文档来了解:http://yubico.com/files/Security_Evaluation_2009-09-09.pdf

要注意的是,yubikey每次插拔都会在内置的非易失性存储器上的Session Counter增加1,该Session Counter总长度为16bit,在每天插拔20次的频率下,该Counter在9年左右到达满值。这个寿命还是很让人满意的。

yubikey是一个非常开放的硬件设备,它本身的硬件实现原理、服务端架设代码、服务端API代码,都是完全开放的。从而在它之上衍生了很多开源项目与之搭配使用。从yubico的wiki页面可以看到一个简单的列表。稍微列举一下:

  • YubiRADIUS:使用yubikey产生的OTP验证用户的RADIUS服务器。该服务器由yubico提供,任何拥有yubikey的用户都可以在上面开通管理员帐号进行使用。详细使用指南:http://wiki.yubico.com/wiki/index.php/Applications:YubiRADIUS_RADIUS_Service
  • RADIUS on Premise:使用LDAP维护yubikey ID与用户的联系,自行搭建使用yubikey做验证的RADIUS服务器。OTP+VPN+RADIUS,这几乎是最安全的上网方案了。
  • YubiKey WordPress Plugin:wordpress登录过程中可使用yubikey配合原密码进行加强安全的登录
  • LastPass Login:使用yubikey作为lastpass的master password,详见@ohsc的日志。该功能需lastpass的高级会员权限,每月$1。
  • Google Apps:使用yubikey登录Google Apps。要是Gmail也能用yubikey登录,那就nb了…