密码强度分析

密码对抗的目标是穷举

密码的意义在于,给我们一种方法区分不同的人:知道某些信息的,和不知道的。因此密码的信息量越大,区分两者的能力越好。

理论上说,对于不知道密码结构的攻击者,最多需要尝试多少次才能解开密码,其次数对二的对数即是密码的信息量。信息量越大的密码,在穷举攻击上难度越高。一个令人望而生畏的密码反而不会被穷举。

信息量是相对的,authentication这个单词有14个字母,看似强度很大(65-70bits)。但是一般攻击者都会试图用常用单词和变形表进行攻击,因此实际信息量的估计不超过英语总单词量(14bits以下)。更精确的说,密码的强度应当是攻击者选用的所有方法下的信息熵。从普遍角度来说,当然密码本身的信息越随机,强度越接近理论值,记忆越困难。

多少位密码是安全的

多少位密码是安全的这个问题其实很难说,取决于两个因素。

  1. 系统可以忍受多快的重试。
  2. 丢失对你造成的损失。

一般来说,损失越大,能够接受的破解概率越低。例如你也许不会介意自己的订票密码被万分之一的概率破解,但是却未必能容忍自己的银行卡被万分之一的概率破解。

重试速度则取决于系统的性能,机制等。网络系统往往可以忍受1req/s的重试很长时间,一年的攻击量就是25bit左右。但是如果使用了抗重试算法则会降低到1req/min以下。

本地密码则更多的取决于性能。使用强力计算机并行计算往往可以提供1+Mreq/s的尝试,因此本地文件的密码一定要比网络密码强这是常识。

安全的密码至少要满足,你的密码强度比上单位时间的攻击量,大于你能够忍受的破解概率。

例如,时间长达一年,每秒攻击一次,一年可以实施25bits左右次的攻击。对于40bits的密码,破解概率就是1/30k(15bits=30k)。

推荐值

网络上使用的密码,强度从低到高可以分为20/30/40/50/60/70几个档。

  1. 20bits的密码可以用来保护一些并不重要的东西,例如电脑的登录密码(可以被强行重置),不常用的论坛(丢失无损失)。不知为何,银行卡密码是这一等级的。
  2. 30bits的密码可以用来保护一些中等级别的东西。这个强度利于记忆,但是未必绝对安全。通常用于保护一些很常用,有一点保密意义,但是并不是特别在意的东西。例如家里的wifi密码。
  3. 40bits是强密码的最低值。要保护有价信息,最低需要40bits的密码。例如邮箱。
  4. 50bits是网络上的强密码,推荐用于保护高安全性机密信息,例如网银登录密码。或者用于保存一些本地不是特别重要的信息。
  5. 60bits以上的密码在网络上已经没有意义了。这个级别是本地高安全信息的存放最低强度。
  6. 70bits基本可以保证任何东西的安全。

如果要对抗政府机关,请选择128bits密钥。

字符的选择

个人推荐,密码的选择范围为0-9a-z,即小写字母和数字。排除掉o和l。选择范围为34个,信息量5bits。

排除o和l是因为,某些字体下这两个会像0和1。当然你也可以排除掉0和1。

符号在某些键盘上会比较难输入,大写字符亦然。尤其在手机上,每输入一个大写都要额外的按Shift,因此相当于多了一次按键。而且在记忆上又需要加上“xx大写X”的前缀,记忆不便。

汉字密码的迷思

汉字的选择是8000以上,所以可以提供13bits的强度。这样的密码,只要三个字就基本可以满足强密码。

听起来挺好,问题是汉字的输入长度,至少是2-3键一字。三个字差不多6-9键,和8个字符没什么区别。如果使用常用词组,还会降低安全性。

如果系统支持的话,对于中国人来说,使用汉字密码确实很方便,容易记忆。但是设计系统的时候最好不要扯到这种东西,容易出错而且不容易调试。

一个数学问题

用户的密码有n种可能性,攻击者每次尝试的破解概率是多少呢?m次尝试的破解概率是多少呢?

如果你的答案是m/n,考虑一下,你每实验个几次用户就改一次密码会如何?

如果用户改密码足够频繁,答案应该是1-((n-1)/n)^m。直观点带入个数据看看,n=2,m=2。原本是100%破解出,现在只有75%。

如果n=1000000。当m=1000000时,实际破解概率只有六成出头。

上面的数学问题说明几点。

  1. 定期改密对暴力破解有一定的抵抗能力,但是效果并不显著。
  2. 要抵抗某个级别的暴力攻击,必须有相应级别的密码强度。靠频繁修改密码是没用的。
  3. 定期改密对抗的对象是偶然的密码泄漏。

九宫连线的强度

9! = 362880 = 18.5bits。其实会略高一些,因为还有P(8,9), P(7,9)等。但是大多数时候不会使用一些特别的绕法。所以大约是16-20bits,5-6位数字的强度。

机制和强度

抗重试算法

所谓抗重试算法,就是能在不干扰正常登录的情况下,降低重试者的频率。

最简单的算法是5次失败后锁定五分钟,这时的重试频率就降低到1req/min了。另一种方法是失败一次后double登录时间间隔。例如第一次失败后3秒内不能登录,第二次失败后10秒,类推。

抗重试算法的核心值在于失败次数和锁定时间。过长的锁定时间会使得攻击者可以人肉尝试你的登录,把正常用户锁在系统外面。过低的重试次数又会导致客户不慎输错后被锁定。而短的锁定时间和很高的重试次数则会抵消抗重试的效用。

另一些方案则采用验证码来对抗软件自动重试。

抗重试算法的几个细节

首先应当注意,如果打开验证码不一定需要跟着打开失败锁定,但是如果打开失败锁定就必须打开验证码。否则有人会以高频进行重试,导致用户自己无法登录。

其次,注意验证的次序,首先检测验证码,然后检查是否锁定,最后检查是否登录成功。如果失败,返回原因一概是登录失败。从安全角度来说,最好不要说明问题的细节(例如是否存在用户)。否则容易造成信息泄漏。例如使用最简单密码尝试多个帐号,从返回中发现是帐号是否存在(登录失败或者不存在本用户)。或者先检测登录是否成功后检测验证码,登录失败时返回为“密码错误”而非“验证码错误”,导致虽然无法登录,但是可以继续暴力破解密码。

不要耍小聪明,用IP来限制重试。攻击者可能利用上万IP来把你的限制绕过去。要假定攻击者可以做到一些还算正常的异常事件。

不要把计数放到session中,攻击者可能通过放弃session的方式绕过。

另外,抗重试算法必须贯彻到系统的每个密码框上。改密的时候需要重新输入密码是常识吧。那里是否有抗重试?短信令牌是否做了抗重试?

验证码

如果你的网站需要被某些残障人士使用,或者政府规定你的网站必须能够被这些人所用。那么单纯的图片验证码就会产生麻烦。

即使抛开上述不论,验证码也是个很麻烦的问题。太强的验证码会让用户失去耐性,太简单的则会让OCR绕过你的防御。

推荐的方法之一是使用reCAPTCHA。

警告

警告是抗重试算法的升级。简单来说,除了维护一个锁定解除后失败计数外,还维护一个连续失败次数的计数。如果这个计数大于一定值,就给客户发邮件报警,强制客户设定更强级别的密码。

警告和调整可以有效的降低密码位数。例如我们能够忍受万分之一的破解概率,连续失败计数是1000次。那么简单来说,25bits的密码就足够保护系统的安全。因为攻击者试验了1000次后,密码强度就会提高到一个他无法接受的值,所以总破解概率只有1000/(2^25),约1/33000,正好在万分之一以内。

当然,实践中并不推荐这么低安全的密码。但是正确的警告可以大幅增强用户密码的安全性是毋庸置疑的。警告和调整的假定是,不是每个帐号都会被暴力攻击的。所以只需要提高正在被暴力攻击的账户的安全级别,而保持普通帐号的安全性恰好在忍受范围以内即可。

另外注意两个细节。

  1. 记录每次登录,无论其成功失败。记录需要包含尽可能多的非涉密数据,例如登录来源,设备信息,帐号,是否成功。需要特别注意的是,不要记录失败密码。即使记录,也要使用加盐后的hash保存。
  2. 通过记录找出一些恶意尝试的IP,并ban掉他们。

静默攻击

有些设计的不正确的警告和调整算法容易被一种叫做“静默攻击”的方式攻击。具体来说,就是一定时间内将失败次数重置。这导致警告始终发不出去。

我们以银行卡密码为例,分析一下安全性。当然我假定银行卡磁条是会被漏出去的。

银行卡密码一般是6位,必须是数字,共计可以提供20bits的安全。一般一天可以失败五次(或者三次,但是下面的计算过程不变)。作为攻击者,你可以失败四次而不触发警报。一年就可以重试1460次,破解概率为1/685。考虑到一般密码是生日的变形,概率会更高一些。如果你的密码恰好是生日的变形,那么破解概率会升高到1/45。

这是一个不可接受的值。

而且最关键在于——银行系统不能提高密码强度。即使收到攻击警告也只能修改密码,或者干脆冻结户头。这样每次的破解概率始终固定,可以通过大量尝试来提升总体破解概率。即使不断修改密码,也只能让连续百万次攻击的成功率降低到63%。换言之,如果攻击者手里有七百张银行卡,每张卡每天试验4次密码,一年内有六成的概率猜到一个人的密码。考虑到很多人使用了生日作为密码,实际上风险会远远高出这个值。

正确设计的计数是不重置,或者以很长间隔重置的。一般来说,正常用户每年的错误概率是固定的。常用的密码不容易出错,不常用的密码很少输入。因此一个用户一年的输入错误数大约也就是几百,加上一些路过攻击,几千次出错一年是一个正常的值。

另外,这个计数不被密码重设所重置。

密码系统的缺陷

密码系统本身有几个缺陷。

  1. 在非安全环境中是容易泄漏的,keylogger是其天敌。即使是安全环境中,也容易被肩窥或者录像。或者可以换个说法,密码只对抗brute force。
  2. 记忆困难,容易忘记。
  3. 修改密码需要密码,一旦丢失就会失去账户的控制权。

自动化密码重设机制

邮箱,SMS和电话

自动化密码重设机制是上面第三个缺陷的解决方案。这种方案利用一个可以收取信息的工具来收取验证信息,从而证明你就是自己。

注意,这里必须保证“只有用户本人能够接收到用户本人的信息”。如果你的手机已经越狱,那么SMS并不总是可靠。而且大部分邮箱都使用明文发送报文内容,如果你的邮件服务商被人监听或者邮件服务商本身不可信的话,那么账户被盗是必然的。

理论上说,地址也是可以的,只要能够保证只有你自己能够接受到自己的信件,而且还能够接受缓慢的邮递时间。

这里要特别注意几点。

  1. 如果用户的安全邮件地址是一个自己管理的域名的话,请确保这个域名本身是安全的。否则攻击者可以劫持域名的MX记录到他自己的邮件服务器,从而获得帐号。
  2. 如果电话和SMS被配置了呼叫转移,邮箱被配置了转发的话,验证系统不攻自破。所以谨慎提防转发。
  3. 由于大部分只能本人接收的系统都是明文的,而且其发送内容往往会被保留在某个地方很长时间。因此这些系统内只能发送一些短时效的一次性token,然后用户利用token来验证自己的身份。

安全问题

早些年,自动化密码重设机制往往都伴有一个“安全问题”,只有答对了安全问题才能寻回密码。

这个的设计原因是因为,如果没有“安全问题”,只要始终去识别验证码就可以持续的发寻回密码邮件,会对系统造成干扰。而且在某些时候,邮箱会被短暂的破解。如果没有安全问题,帐号就会很快丢失。

但是安全问题的设计其实很扯淡——大部分时候,如果我能记得安全问题,我就能记得密码。实际上在大部分系统上的问题都是——压根不记得我的安全问题里面回答了什么。

而且现在随着SNS的流行,找到安全问题的答案越来越容易了——母亲的姓名和生日?小学老师的姓名?这些都不难被找到或者通过简单的社会工程学弄到。如果选择使用安全问题的话,一定不要选择填写一些公开或半公开信息,例如上面的亲属生日等等。因为很容易被找到。

当然,由于密码重置攻击的结果只是造成系统讨厌的垃圾邮件而已,所以使用这种攻击的人并不是特别多。至于“邮箱属于用户本人”基本已经成为了共识。大部分用户可以认同“邮箱被盗就等于所有账户被盗”。因此个人意见,安全问题不做也行。

慢重置

同样是对暂时破解邮箱(或者手机——这更频繁)的解决,慢重置利用减缓重置速度的方法来抵抗攻击者。这个思路不仅适用于自动重置,而且适用于人工重置过程。

想象你被劫持了,绑匪弄不到你的银行卡密码,但是他们拥有你的身份证和银行卡。他们是否可以通过身份证代办的方式来重置你的密码(假定密码重置不需要本人亲自到场——很多时候本人亲自到场是有困难的)呢?

银行对抗这一问题的方法是,首先必须挂失。在挂失一周后才能重置密码。当然,在大部分业务里面一周太长了。但是长达小时级别的通告-重置周期是值得考虑的。几个小时的时间足够真正的用户看到重置动作通知,并做出合适的反应。立刻进行重置的话,用户不一定总是能看到。甚至在看到并产生反应的时间内,攻击者就造成了损失。

慢重置的问题在于对于重置的响应很慢,这会使得正常用户很不愉快。所以建议只有真正需要高安全性的系统才使用这种方案。

社会关系验证

社会关系验证出名的有facebook和qq两家,其中qq的实现还是有问题的。

所谓社会关系验证,实际上就是从你的好友中挑选N个人,如果你能够证明其中M个确实是你的好友,你就可以通过验证。

证明的方法很多,例如你通过别的方法通知对方,让他们知道那个验证请求确实是你。facebook采用的是一堆图片里面找到哪个人是对方,有的时候碰到一堆乱标的图片会很火大。

注意,这里挑选N个人,必须是系统随机选择,而不能是客户自己来选择。否则就会产生M个好友一加就能抢掉你的帐号的问题。

自动化密码重设系统的缺陷

自动化密码重设系统实际上等同于密码验证,因此如果自动化密码重设系统存在缺陷,密码系统再强也没用。所以自动化密码重设系统的强度必须大于或者等于密码验证。而实现上说,大部分系统的强度是远不及密码系统的。

从设计者角度来说,最好的办法就是不做自动化密码重置。以下是几种替代流程,当然,他们各有缺陷。

  1. 通过用户的物理接触,核对一些信息,再触发密码寻回流程。这样的成本很高。
  2. 预先发送的密码信封,里面封装了一个revoke token,可以触发寻回流程。这其实是安全问题的变形,但是更不容易猜出,也相对不容易丢失。但是一样需要线下机制辅助。

从用户来说一定要小心。随着你的手机和邮箱绑定了越来越多的寻回,一旦邮箱和手机丢失是一件非常恐怖的事情(尤其是手机,因为很多邮箱都绑定到了手机)。所以如果绑定了大量寻回,邮箱必须强密码,手机必须打开加密(丢失后整个机器无法读取),而且千万别越狱。

增强验证

TOTP

增强验证是试图解决密码的缺陷一,使得在非安全环境中密码即使泄漏也不会造成帐号被盗。大部分增强验证都是基于TOTP(time-based one time password)的。

首先,服务器和某个安全的设备共享一个密钥,并且保持时间同步。然后在登录时,安全的设备显示密钥加密过的当前时间,只要和服务器上的密钥加密过的当前时间在一定差以内,就算通过。

问题在于安全设备。目前gauth使用手机,这其实未必安全。真正的安全设备必须是特别设计实现的硬件,例如工行的电子令牌。

几个TOTP实例

google authentication就不说了,目前受到广泛应用。

国内做OTP有两家比较大的公司,坚石诚信和飞天诚信。

yubico公司出品的yubikey是个很有趣的产品。这个产品一个U盘模样的硬件,把自己伪装成一块键盘。当用户需要输入OTP时,只需要按一下按钮,yubikey就会自动模拟按出对应的键。因此这个产品可以免去用户手工输入的繁琐,而且支持几乎所有操作系统。

利用自动化密码重设的机制来增强验证

有些时候,我们可以利用自动化密码重设的机制来增强验证。例如登录时需要输入手机短信令牌等等。

实际上这种设计并无必要。因为自动化密码重设机制等效于密码本身,利用密码来增强密码是没有道理的。以手机短信令牌为例,我们可以想想。如果丢失手机,那么得到手机的人可以重置密码来完成登录。如果手机没有丢失(即手机始终是安全的),那么可以只用手机而不需要密码,强度并没有降低。正常情况下我们不这么做的理由是因为收SMS看手机会比较麻烦。可既然每次登录都必须输入验证码,为什么还需要密码呢?

一个比较可行的方案是,利用一种渠道来重置密码,而利用另一个来增强验证。那么这两个通讯渠道必须都保证安全。

客户证书

客户证书可以为我们提供一个强验证机制,并且可以保证通讯过程中的安全。不过遗憾的是,大部分客户证书的实施开销都很高。

软件证书相对容易使用,但是很容易被从计算机中窃取。硬件证书可以规避窃取问题(如同TOTP一样),但是成本很高。而且大部分硬件证书都有兼容性问题,无法用于windows以外的系统,甚至IE以外的浏览器。

客户证书最麻烦的问题,在于大量客户证书的发行和管理很麻烦,而且通讯过程中的密码学计算需要大量资源。

增强验证的缺陷

独立硬件的增强认证系统的麻烦在于,需要随身携带令牌防丢失。而且为了防止丢失——这几乎是必然的——所以需要一些额外的验证码作为丢失时的报废手段。而这些密码一旦丢失,其结果和令牌泄漏是一样的。不过幸好,大多数情况下都不需要输入紧急密码,更不会在非安全环境输入。

而且TOTP必须每次都看一下手机或者令牌(yubikey例外),这对需要频繁输入的密码是很不利的。

从用户角度说应当怎么做

每个网站使用不同密码

即使不对每个网站使用不同密码,至少也要将网站分组,使用不同密码。绝对不能对所有网站使用同一密码。

上文可以看到,不同密级的密码长度是不固定的。建议普通网站8个字符,高安全性网站10个字符,最高安全性网站12个字符。(每个字符5bits计)

对不提供防重试,警告功能的网站提升密码强度,至少要提升10个bits的安全性。

定期修改密码

定期修改密码的主要目标是抵抗偶然的密码丢失,例如非安全环境,肩窥,或者偶然被人猜到。

周期取决于偶然事件发生的概率。即使没有什么你可以察觉的偶然事件,三年修改一次密码也是个良好的习惯。

避开01a

虽然听起来很愚蠢,但是大部分brute force程序都会从11xxxx或者00xxxx,aaxxxx开始枚举,而不是按照数字或者字母频度表排序。因此在密码的前面使用01a其实略微增加了被枚举的概率。

对可以打开OTP的服务启用OTP

以下是一些著名的使用了gauth的网站。

  • google
  • facebook
  • github
  • evernote
  • dropbox
  • ssh

以下是一些自己实现了OTP的例子:

  • 支付宝(宝令)

不引入无效的麻烦

这个逻辑在“利用自动化密码重设的机制来增强验证”里面出现过。不需要用同等安全性的方式来加强安全性。

例如手机宝令和短信令牌。在手机安全的情况下,两者都是安全的。当手机不安全了(被植入程序或者丢失),两者都不安全。因此同时启用两者并不增强安全性,只增加麻烦。

实际上只要引入其中比较好用的一个就行了。相比宝令,短信受环境影响更大,因此建议关闭短信令牌,只启用宝令。

小心处理安全通讯方式

主要说的是主邮箱和手机。用于安全性加强的联系方式往往会被用户配置为互相加强的情况,这是非常危险的。安全中最危险的模型就是环状模型,一旦一个点漏了整个环都会崩溃。

例如两个邮箱,A邮箱可以被B重置,B邮箱可以被A重置,又没有其他的保险措施。于是当其中任意一个发生泄漏的时候,所有的帐号就像被接到了一起的鞭炮一样,一个也跑不了。

通常而言,建议的方式是星型汇聚——多个安全的联系方式用同一个基础作为寻回。一般来说,就是多个邮箱利用同一个手机做寻回。或者多个安全方式之间不做寻回加强——因为反而不安全。

对于星型汇聚,当这个点出问题时所有帐号都会面临风险这点不变。但是当某个邮箱出问题时,其他邮箱并不面临风险,同时(可能)可以通过手机寻回密码。

对于不做寻回加强,万一邮箱被盗,相关帐号就无法恢复了。但是至少这风险不会蔓延到其他邮箱的帐号上。

——致命性标靶的面积总是越小越好。