续谈登录相关数据的安全加密策略——密码泄露事件杂谈之二


安天实验室 APM开发小组

(文:江海客 校对:Bughouse)

 

作为WEB应用密码存放示范程序的AntiyPassword Mixer开源发布之后,我们陆续收到微博私信、电子邮件等。令人觉得有些意外的是,希望我们评价其现有加密方式是否合理的网友,要比对APM本身设计提出意见的多。出现这种情况的原因很好理解——WEB应用的最大压力有时并不来自开发,而在于保证业务持续稳定运营和兼容发展。比起换一个更安全的方案,先评估目前方案的安全性肯定更直接。

但遗憾的是,由于收到的方案多数是有问题的,但又不便于逐一点评,因此我们撰写了本文。假如之前《由脱库攻击谈口令字段的加密策略》一文的内容您已经充分了解,那么基本上并无再读本文的必要。

此外,网上一直在讨论口令经过算法变换的结果集合应该叫什么。在主机攻防的时代,我们随口叫称之为shadow,或者SAM,甚至口误说成NT的Shadow之类,都不会有什么风波,因为大家都能意会。但落实到算法这个很严谨的领域,要面对各种不同的应用,我们又有些犯难。从严谨的密码学的角度,经过单向的HASH算法的变换结果,应该称为散列值或哈希值;而只有可逆的加密操作所生成的结果,才叫密文。但实际上,目前用于口令变换的方法是五花八门的(其中不仅有各种HASH、也有DES或者AES、亦有把对称算法当成散列算法用的)出于通俗的角度,我们一直没有回避使用加密和密文这样的语汇,这虽然并不严谨,却可以让多数人理解。

从HASH计算速度实验开始

摆在我们面前的计算机到底有多快?我在半夜23:30突然给APM开发小组的老张同志提出了这样一个问题。我希望知道一台普通的台式机或笔记本针对明文口令每秒可以执行多少次HASH运算。(我提醒老张同志,超过10分钟给我答案,就会有损他的英明神武、就会让他喝不到他最爱的百事、就会公开他的裸照等等。)

于是,老张在第八分钟给出了下面的答案。

主机环境:

Core 2 (T7250) 2.0 G ,2M cache, 4GB RAM, Windows server 2008 64位

虚拟机环境:

Vmware Server, Ubuntu 10.04, 512MB RAM

 

在虚拟机中针对16个字节的数据计算MD5,2.99秒内共1759393 次。

这是一个令人震惊的事实,除去磁盘I/O等因素,一台非常普通的个人计算机针对口令这种短字符串每秒都可以进行超过50万次HASH运算。这里没有并行计算、没有云计算、没有GPU计算,它甚至只是一个虚拟机。

我们还可以在这里找到其他环境下的结果:http://pastebin.com/WGKgpsga。比如:

Intel Xeon CPU E5520 2.27GHz

 

Doing md5 for 3s on 16 size blocks: 3787525 md5's in 3.00s

这就是摆在我们面前的计算机。尽管在玩类似于《使命召唤》之类游戏的时候我们还会抱怨它配置不够强劲,但实际上它已经比想象的快得多,至少在做某些事情的时候。

我们又找出此前的一次综合性测试,它对比了不同HASH算法、不同大小计算对象的速度,部分结果如下:

测试数据大小

692 bytes

51KB

550KB

27MB

371MB

算法/位数

毫秒/次

毫秒/次

毫秒/次

毫秒/次

毫秒/次

MD2/128

0.1453

10.2

110.62

5578.1

75550

MD4/128

0.0015

0.15

1.32

65.6

898

MD5/128

0.0023

0.23

2.03

103.1

1421

SHA/160

0.0039

0.31

2.89

147.6

2008

SHA1/160

0.0031

0.31

2.96

150

2063

SHA224/224

0.0109

0.93

9.06

462.5

6336

SHA256/256

0.0109

0.93

9.23

456.3

6383

SHA384/384

0.0242

1.63

17.11

859.4

11836

SHA512/512

0.025

1.63

16.87

857

11758

CRC32/32

0.0015

0.15

2.03

96.8

1320

CRC64/64

0.0046

0.16

2.75

147

2031

(上述结果节选自2010年3月安天实验室《基于内存对象对26种HASH方法的测试结果》,测试环境为:AMD Athlon 64X2 4000+ 2.09GHz, 4GB RAM, Windows XP Professional SP3。)

通过对更长的数据计算HASH可以看到,HASH并非天生飞快,数据长度以指数增长时,HASH速度也会以指数变慢。但回头来看,在人的记忆力和忍耐力能接受的口令长度上限以内,HASH必然会被极快的运算着——快得异常强大。

遗憾的是,这种强大的资源不仅为我们所有,也为“神秘河”对岸的他们——那些在窥视着我们的数据与虚拟资产的攻击者——所拥有。

而且,如果我们不是某个超算中心的主任、不是亚马逊计算云的管理者、不是银河甚至天河超级计算机的设计者……我们就需要知道,他们拥有的计算能力比我们多得多。

他们所拥有的能力,我们在前篇中已经论述过。在计算能力上包括:超算资源和其它的在线计算资源、GPU加速、僵尸网络上的大量可控节点;在数据资源上包括:已经泄露的大量明文用户名口令数据、高频密码档、本地的巨大彩虹表、在线的各种查询接口+库等等。

关于抵御彩虹表和在线查询

“彩虹表”变成大众语汇,似乎就是从本次事件开始的。

百度百科对彩虹表的解释是:

彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合。不一定是针对MD5算法的,各种算法的都有,有了它可以快速的破解各类密码。

前半句很科班,后半句很教程。

但这句教程并不多余,它提醒网站的开发者们不要把后台口令加密机制的缺陷简单当成用不用MD5的问题。具备再次查询价值的常见算法均会被攻击者自发乃至自觉地做成彩虹表,这就是我们必须面对的现实。

MD5用于口令变换保存遭遇的问题与散列强度是无关的。有网友此时提及王小云教授的MD5的散列值碰撞研究成果,但这与拖库事件没有任何关系。王小云教授对于MD5破解的研究成果是可以构造出两个不同的内容其MD5值相同的明文。但到目前为止,所有公开的研究成果都没有展现出存在可以根据一个已知的MD5构造出明文的情况。只要这种情况尚未出现,口令安全的尴尬就不是由MD5的算法缺陷导致的。我们在前篇已经说明,HASH在口令保存问题上遇到的困境很大程度上来自口令是一个有限集,因此他是难以抵抗生成和查表的。

看到有IT同仁在博客上推荐换用SHA1,这实在是一个误导。SHA1的彩虹表不仅存在,而且同样强大。不仅如此,一些常见的WEB系统和应用场景下固定SALT值HASH和一些HASH的交叉组合、微软使用的NTLM和早期UNIX所使用的非散列的DES加密结果都被制作出了相应的彩虹表。

由于散列值必须和明文存在对应关系,因此基于HASH的结果是不可能对抗查表的,在这一点上任何HASH算法都会遇到同样的遭遇。

也许未来的HASH算法需要考虑提升彩虹表的生成代价。在老张的惊魂八分钟后,我们开始讨论是否能设计这样一种HASH,其计算需要极为漫长的时间(比如3秒),但其又不会造成严重的CPU负载。然而,经过讨论,从设计、实现到应用的前景我们都对此表示悲观。由于网站的效率和体验问题,没有任何开发者愿意接受一种需要3秒钟才能完成的密码验证算法。更无法想象,如果有几百个用户同时登录,系统就会骤然卡住的情况。

而同时,我们怎么来实现一个算法的既慢速而低负载呢?我们的结论是,即使假定我们利用X86架构的某个特性实现了这一点,那么一旦具备破解价值,也会有人做出相应的HASH构造专用软硬件出来。此外,我们会因为如之前引文的测试表现,因MD2最慢而改用MD2么?这是否是因为MD2的相关实现而没有得到足够的优化呢?我们最终在APM所用的HASH选择上还是用了SHA256,但确实与它要比MD5慢一些无关。

从马上就可以上手的角度,我们有些自欺欺人的可以告诉网友,在算法(代码)不可知的情况下,能够单向的不可逆的扰动HASH结果的方法,都可以一定程度上抵御彩虹表。但这却是一个无比高难的问题,请再次注意这句话的前提条件——算法代码不可知。因为很多拖库攻击正是从获取了CGI程序所在服务器的访问权开始的,源码都被copy走了,何况其中的算法流程呢。如果是开源产品或者通用WEB产品,要做到不让别人知道就更不可能了。

同时,我们还需要看到很多扰动方法的设计本身是不科学的。从数学的角度,我认为,任何这些简单的迭代变化,包括一些补齐、代换、轮换和剪切的操作,实际上并不会比经过严格推敲的HASH算法本身更理想,也不会增加HASH的复杂度。相反,它们基本都会从数学意义上弱化HASH的强度,包括在HASH上再取HASH之类的方法。从对抗重新生成彩虹表的角度来说,只要算法泄漏,都没有实际的价值。

这些方法的可靠度,还不如规规矩矩地加盐(SALT),每个站点有自己的盐肯定是必须的,因为降低攻击者制作彩虹表的兴趣的最好办法,是让其计算资源的投入没有复用价值。

关于抵御统计攻击

我们认为,无论如何,采用加盐的方式,要比自己做一些自以为很完美的变换处理要科学。但我们收到的方案里,大部分是只考虑了一站一盐的。这固然能抵御现有的彩虹表,但即使攻击者只获得了库、而没有获得明文,问题也很严重。首先就是统计攻击问题:

Troy Hunt在对索尼用户密码泄漏出的资料进行分析Brief Sony passwordanalysis一文中统计出了常见的25个密码(很尴尬的说,其中好几个单词我都不认识,也许这些看起来很眼生的词可能与索尼游戏场景有关吧):

seinfeld, password, winner, 123456, purple, sweeps, contest, princess, maggie, 9452, peanut, shadow, ginger, michael, buster, sunshine, tigger, cookie, george, summer, taylor, bosco, abc123, ashley, bailey

常见的口令肯定是与国家、文化、具体站点的内容相关的。比如国内用户可能更为常见的是888888之类的密码;比如CSDN事后就有人在常见密码的统计中就出现了一个在CSDN论坛很知名的、带有美女头像的男网友,“他”的名字叫xiazhili。

如果攻击者会对密文进行频度统计,列出了TOP100;然后从一些背景相类似的已知明文库也计算出明文的TOP100;最后攻击者找到一些与TOP 100密文对应的用户来尝试这些常见明文密码……

后面的事情,并不用我们再说。考虑到国内用户缺乏足够的安全意识,123456和888888比比皆是,即便只有3%的用户密码被统计分析破解,考虑到攻击者连算法都不知道的情况下做到的这点,这个结果依然是很悲惨的。

所以我们有必要提醒WEB设计者的是:

任何一种以同样的算法和参数对口令做变换的方法,变换的结果集都是不能对抗统计分析攻击的。

对相同的明文口令,要做到密文结果不同,这是“脱库”时代对设计一个口令加密/变换模块的基本要求。

关于抵御海量已知口令碰撞

明文相同,密文不同,这个要求不仅是对抵御统计分析意义重大。一站一盐的方式在只泄漏库的情况下还不是最坏的,让我们来看看攻击者如果同时获取了算法和参数会如何?实际上这种问题并不罕见,攻击者也许并不能理解你的算法,但他获得了网站的代码后,就会把类似的代码作为黑箱,直接调用进行计算。

无论你的加盐方法如何,基于我们在第一节所描述的强大计算能力,攻击者就可以基于常用口令或者字典批量生成密文,然后和已知的库进行碰撞。网站的设计者如果以为攻击者会像传统的暴力破解一样,设定一下长度的上限、设定一下是否数字、字母、字符,然后来穷举,那就想错了。不要忘记他们手中已经持有的高频密码表,更不要忘记他们手中已经拥有的海量明文密码表,这才是他们调用算法生成密文的起点。

假定攻击者手中有1万个高频密码,而实际上你的用户设定密码中有20%与这些高频密码重合,他从这些密码根据你的算法(可能就是在你应用的源码上修改)来生成一份密文对照表,在非常短的时间内,攻击者就可以获得这些用户的明文口令。

再假定攻击者手中有1000万个常用密码,而你70%的用户的密码都在这些之内,根据前面的计算速度,即便把I/O时间都算上,生成一份密文列表也不会花费很多时间。

在算法和加密参数无法保密的情况下,单独尝试对口令字段做出任何方式的双向或单向的加密处理,都是容易遭到明文口令集合的碰撞攻击的。

因此目前APM使用了用户名+针对这个用户随机生成的一粒盐作为这个用户最终的SALT。但如果没有其他的盐,只做用户名和密码的组合是不够的,如果你注意研究过相关资源,你会发现确实有彩虹表根据具体的场景是依靠某个标准算法对用户名和密码组合的加工结果来制作的。所以必须至少引入一粒口令以外的个性化量,这个量应该是不能随意修改的,从一些开发者的实现经验来看,除了用户名外还可以把一些不变的属性,比如用户序号、注册时间等作为盐的组成部分,掺入最终的HASH密文生成中,都是比较有效的方法。当然参与进来的量越多,提升的是对攻击者获得代码后的分析复杂度,但对于迟滞碰撞时间的影响并不明显。

个性化盐引入后,即使攻击者拖到了全部资源,也只能逐一地验证每个用户的密码是不是在某些明文之中,而不能判定哪些用户的密码是相同的。也就是说攻击者需要基于一个明文密码列表,对每个用户都实现一遍同样的密文生成操作,而不能一次性完成对密文的碰撞。针对每个用户生成的密文结果,只能与这个用户的密文值对比,而无法与其他用户的密文对比。

另一方面,如果攻击者拿到了两个同样采用APM的站点的数据库,即使同一个用户在两个网站的用户名和设定的口令是相同的,其密文也不同的。

基于这些,攻击者的兴趣就会一定程度上减弱。

这也引出了微博上网友提出的另外一个问题——假如做到了用户名与口令联合生成HASH(以用户名为SALT),那么辅以一站一盐是否就足够了,就没有必要生成盐表了呢?

我们认为盐表可以做一个单独的安全层次来设计。对于一些针对单表DUMP的方法,会导致攻击者拿不全数据。除此以外,在攻击者拥有全部数据的情况下,APM的盐表方法面临的威胁与上述方法基本是一致的。

但我们使用盐表还有另一个考虑。尽管我们不愿意在任何场合都谈“程序、数据和配置三分开”的原则,但一站一盐的代码,很容易导致使用者不去修改初始的盐值。即使盐是写在外部的,也很容易最后演变成把固定盐值写入代码,然后导致多站一盐的情况。而这会导致根据这个固定盐值得用户名+口令的组合彩虹表的出现。

回头来看,无论针对每个用户加入了怎样不同的盐,虽然这样可以把依据明文口令列表构造破解的总时间变成了需要乘以用户数N,但依托一个短小的常见高频明文密码列表来碰撞的时间依然很短。

因此,网站依然需要改进其注册策略,提醒用户尽量不使用常见密码,而这个常见密码的范围,实际上已经比传统的常见口令大的多。

关于一些策略技巧

在0DAY横行的时代、在我们使用着大量第三方应用的时代,不要幻想仅靠某个算法或某个实现解决问题。你是否想到过,搞一点小技巧也许(也仅仅是也许)会有一些有趣的效果?

比如网上账户和柜台账户的密码凭证分开,虽然对少数网银已经做到了,但对类似手机网上营业厅、一些航空公司的会员等,都依然存在依然靠4或6位数字密码来作为口令的情况。这是由于传统的柜台系统都是甩一个小的数字键盘在柜台上,让用户输入密码。这对有监控摄像、有人工身份核对的柜台交易或许已经够了,但对网络攻击的挑战来说,就是太脆弱了。

比如在系统中潜伏一些账号。假定你是一个方兴未艾的中小规模论坛的站长,目前有一万用户,但假如你做一个后台的批量注册接口,再均匀掺入九万个“潜伏”用户会如何?如果攻击者不挑出这些用户,就要更多的付出碰撞的时间(前提是先做到相关的安全要求)。

又或者你构造出一些用户,并把其中一部分放在用户列表的最前面,而在保存他们口令散列值的字段里存放一些已经存在于常见彩虹表中的散列值。如果攻击者上来就查表,而不是读程序的话,他也许会被误导,因此错误地判断你真正所用的算法。此外,如果你把这些虚拟用户的登录信息作为审计条件的话,当这些用户被那些对应的明文口令尝试登录时,你就知道自己被拖库了。

我们并不知道攻击者是否比我们更聪明,但假如我们比他们更勤奋一些,也许结果会有些不同。

我们也相信,在这些具体的策略方面,应用开发者会比安全工作者富有更多的智慧。

关于一号通

如果说什么是在设计APM时最沮丧的,那就是我们解决不了如下的问题,即在攻击者获得了APM加密方法、参数和全部的库之后,无法解决用户“一号通”的问题。

这里说的“一号通”,就是指用户在不同站点使用完全相同的用户名和密码的问题。

也就是说,无论采用何种方式,对于那些已经在其他站点被暴露的用户名和密码组合,如果在APM的体系下存在,依然会被以极短的时间联合构造碰撞出来。

真的是这样么?是的,无论加入多少变换方法都没有意义,一旦攻击者获得了完整的资源的话,无论引入多少个性化量也都无法解决这个问题。因为,任何个性化量必须建立和用户的对应关系,这个关系同样是需要存放在库里的,而拖库的攻击者亦能获取这个关系。

我们真的不能通过算法解决这个问题么?真的不能!必须提醒用户的是,我们能设计出的安全实现,仅限于在算法公开和密文被获取的情况下保证安全,却不可能在算法、密文和密钥均被获取的情况下保证安全。

目前我们正在新版本的APM中尝试提供这样一个方案,应用开发者亦可考虑一下——不保存用户名,用户均显示昵称,并且建议不允许用户名和昵称相同。这样会规避直接的碰撞搜索,但也无法保证其他的用户信息不会被攻击者做分析关联,之后再做数据联合碰撞。

对于目前的APM已经有朋友抱怨过于复杂,也可以想象,假如一个已经投入运营的站点要进行我们设计的新方案这样的更改,有多大的工作要完成。但世界上真的没有这样一种食品,它不用洗就能吃、它色香味俱全、吃了它强身健体、吃了它有百利而无一害、吃了它百毒不侵……...

我们很清楚:让攻击者呕吐的复杂方案,首先会让开发者呕吐。但是,是自己在夜深人静时,对着墙角狂吐不止;还是躺在大街上,成为众人的呕吐对象——这是我们需要做出的选择。

补记一:

要求用户为每个网站设置不同的用户名和密码组合是悲剧的、是残忍的,有个网友对我说:“密码这么多!记到脑子里怕忘了,记到本子上怕丢了,记到文本里又怕被木马偷了,怎么办?”

我想向他推荐一款我用过的带有加密芯片的U盘,但突然转念一想,万一他东西都放在U盘上,一旦U盘丢了、或者U盘口令忘了,那不就四大皆空了么?

人人自危的时代,是职业安全工作者的耻辱,我们知耻后勇,方能有所作为…….

补记二:

俺们的老张同志指出了文章的一个名词问题,备忘于此:

 

一般来说,计算速度不使用每秒多少次为单位,而采用Mbps(兆位每秒)或者MB/s(兆字节每秒)。

对固定长度的数据来说,这两种度量方式下的结果的转换关系是:

长度 * 次数/秒 =  数据量/秒

如果从使用Mbps和MB/s的角度出发,学术的角度来说,诸如MD5等算法从设计上是现行复杂度的,也就是说计算速度应该是与数据大小无关的常数。而从实践的结果看,是越短的数据计算HASH的速度越慢,越长的数据计算越快。这有两方面原因:一、计算HASH分3步:初始化数据、计算、收尾计算,初始化和收尾计算的时间是常数,因此对短数据计算速率的影响相对更大;二、连续的大段数据在计算时总线请求、内存访问、cache优化等方面有更大的优势。

但从密码破解的角度,确实是以单位时间的计算次数(即次数/秒)来衡量才有价值。从这个角度来看,越短的数据,在单位时间的可以计算HASH的次数越多。文中计算HASH速度的概念,实际上是一种类似于频率的概念。



参考资料:

[1] 王小云等. Collisionsfor Hash Functions MD4, MD5, HAVAL-128 and RIPEMD.

[2] Troy Hunt. BriefSony password analysis, http://www.troyhunt.com/2011/06/brief-sony-password-analysis.html

[3]AntiyPassword Mixer, http://code.google.com/p/password-mixer/

[4] 安天反病毒引擎研发中心. 基于内存对象对26种HASH方法的测试结果. To be appeared at http://code.google.com/p/password-mixer/downloads/list

 

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值