原文: Hacker, Hack Thyself
作者: Jeff Atwood
译者:陆其明,爱奇艺公司技术总监,拥有10多年的软件技术研发和管理经验。已经出版的著作有《DirectShow开发指南》、《DirectShow实务精选》、《Windows Media编程导向》、《脚本驱动的应用软件开发方法与实践》,译作有《代码之道》、《高效能程序员的修炼》、《程序员的修炼——从优秀到卓越》。
因为安全漏洞,社区网站被攻破或摧毁的案例已经不在少数。也因此,我们在立项做Discourse.org的时候,就把那些教训铭记于心。尽管开源的社区软件已经成千上万,我们还在为开发一个在骨子里就非常安全的软件而努力。
与此同时,我们也重视可移植性——你能随意将数据从Discourse.org导入或导出。这也是Discourse遵从Creative Commons协议的原因——这一点有别于其他的论坛软件。作为一名普通用户,你可以在用户信息页面,轻轻松松地将你在Discourse上发表的文字导出并下载下来。
注:Creative Commons(知识共享)是一个相对宽松的版权协议。使用者可以明确知道所有者的权利,不容易侵犯对方的版权,作品可以得到有效传播。
如果你是站长,你可以在管理面板上轻易地备份或者还原整个网站的数据库。在浏览器里操作就行。我们一开始就为你设置好了每周的自动备份。为你考虑得这么周到,算是你有福了!我可是亲身经历了一次惨痛的教训之后,才在备份方面成为行家里手的。
这么多年下来,我们意识到,在安全和数据可移植性之间求得平衡是很难的。能把整个数据库下载下来,最开心的莫过于黑客了,这意味着他们很快就能在你的系统中获取据点。那可是终极大奖!
为了缓解这种威胁,我们对Discourse的备份在很多方面逐步加强了限制:
说到“安全”措施,无非就是深度防御,因此所有这些加固手段都是有益的……但是,我们仍然需要假设,网上的一些坏家伙能够通过某种方式获得你的数据库的一份拷贝。然后怎么办呢?好吧,我们看看数据库里有些啥?
众所周知,cookie是浏览器用以识别用户身份的。cookie通常以哈希值的形式存储,而不会是裸值。因此,暴露哈希值不至于让你冒充目标用户。况且,大部分现代的web框架都会快速回收cookie,因此它们的有效期只有短短的10~15分钟。
尽管用户对于自己的email被曝光这件事,多少还是有些担心的。但现如今把email地址当成至宝的人应该也为数不多吧。
我们假设这是一个完全公开的网站,没人会在那里发布特别敏感的东西。既然所有的文章都是公开的,我们也就不必担心什么商业机密或其他秘密资料被揭露了(至少眼下不用担心)。如果我们确实有一些机密资料要保护,那我以后可以专门写一篇文章来探讨这个问题。
剩下的就是密码的哈希值了。而且,那确实是个严重的问题!
现在的问题是,攻击者拿到了你的数据库,他们通过大规模的离线攻击,或者在他们财力能够承受的范围内充分利用云计算资源,攻破了密码哈希。一旦他们得逞了,他们就能以那个用户的身份登录……一直这样冒充着,直到那个用户修改密码。
★ 所以说:如果你知道(甚至只是怀疑)你的数据库被暴露了,你应该做的第一件事就是重置所有人的密码。
然而,如果你没有意识到被拖库了,怎么办呢?你需要先发制人,像世界最流氓大公司的IT部门那样,每隔30天就重置所有人的密码吗?那样的用户体验显然很糟糕,还会把自身带入一种严重的病态。现实是,当你的数据库被暴露之后,你可能并不会知道,而等你知道的时候已经太晚了,那时采取任何措施都已枉然。因此,关键还在于拖慢攻击者的节奏,为自己争取时间去处理和应对。
这样的话,你可以提供给用户唯一真正的保护,就只有你存储的密码哈希的抗攻击性了。说到哈希的强度,有两个因素:
我看到过这样的建议,说你应该把全局的工作因子设得足够高,以至于在目标平台上计算一个密码的哈希值至少需要耗时8毫秒。事实证明,Sam Saffron(和我一样是Discourse的联合创始人)早在2013年选择NIST(美国国家标准与技术研究院)推荐的PBKDF2-HMAC-SHA256和64k迭代,是多么明智的一个决定啊!我们测试过,这个算法在我们目前的服务器(相当高端的Skylake 4.0 GHz)上,运行我们现有的Ruby登录代码,确实需要耗费大概8毫秒。
不过,那是4年前的事了。现如今,我们数据库里的密码哈希还这么安全吗?4年以后或者10年以后呢?我们在做的是一个开源软件,是一个长期项目,我们需要确信我们做出合理的决定,以保护每一个人。俗话说得好,防人之心不可无,是时候戴上达斯头盔演一回坏人了——让我们自己来攻击自己的哈希!
我们先用恶贯满盈的单GPU(GTX 1080 Ti)小试一下。先给些参考数据:PBKDF2-HMAC-SHA256在1080型号上可以达到1180 kH/s(千次哈希/秒),而在1080 Ti上可以达到1640 kH/s。看吧,还是同一代视频卡呢,在攻击哈希的效率方面竟然能提升将近40%。细思极恐!
首先,我们来做一个小小的试验,看看是否行得通。我下载了Hashcat。
注:Hashcat是当前最强大的开源密码恢复工具,可以访问Hashcat.net了解更多详情。
我登录进我们的演示网站try.discourse.org,然后创建了一个新帐号,并设置了密码0234567890。接着,我查看了数据库,发现为那个新用户生成了如下的哈希值和盐:
hash
93LlpbKZKficWfV9jjQNOSp39MT0pDPtYx7/gBLl5jw=
salt
ZWVhZWQ4YjZmODU4Mzc0M2E2ZDRlNjBkNjY3YzE2ODA=
Hashcat对输入文件的格式是有要求的:一行只描述一个哈希,须注明哈希类型,迭代次数,盐和哈希值,并且各部分之间用冒号隔开,如下:
sha256:64000:ZWVhZWQ4YjZmODU4Mzc0M2E2ZDRlNjBkNjY3YzE2ODA=:93LlpbKZKficWfV9jjQNOSp39MT0pDPtYx7/gBLl5jw=
让我们把它交给Hashcat,看它是否搞得定!
./h64 -a 3 -m 10900 .\one-hash.txt 0234567?d?d?d
注意:这里故意降低了计算量,只让它猜3个数字。而不出所料的是,我们很快就把密码攻破了!看到最后的密码了吗?我们得手了!
sha256:64000:ZWVhZWQ4YjZmODU4Mzc0M2E2ZDRlNjBkNjY3YzE2ODA=:93LlpbKZKficWfV9jjQNOSp39MT0pDPtYx7/gBLl5jw=:0234567890
到目前为止,我们知道上面的攻击方法是可行的。然后让我们进入正题。不过,我们一开始先简单一点——蛮力攻击最简单的8位数字组成的Discourse密码需要多久呢——也就108个组合,1个亿吧。
Hash.Type........: PBKDF2-HMAC-SHA256
Time.Estimated...: Fri Jun 02 00:15:37 2017 (1 hour, 0 mins)
Guess.Mask.......: ?d?d?d?d?d?d?d?d [8]
即使用的是一款极品GPU……记住,这里我们只是测了一个哈希,也就是说,表中的每一行(用户)都需要耗费你1小时。还有更打击你的消息:Discourse已经禁用8个字符的密码有段时间了。如果我们尝试破解更长的数字型密码,需要花多少时间呢?
?d?d?d?d?d?d?d?d?d [9]
Fri Jun 02 10:34:42 2017 (11 hours, 18 mins)
?d?d?d?d?d?d?d?d?d?d [10]
Tue Jun 06 17:25:19 2017 (4 days, 18 hours)
?d?d?d?d?d?d?d?d?d?d?d [11]
Mon Jul 17 23:26:06 2017 (46 days, 0 hours)
?d?d?d?d?d?d?d?d?d?d?d?d [12]
Tue Jul 31 23:58:30 2018 (1 year, 60 days)
要知道,全数字的密码模式太简单了,小朋友才会用!要不要试一些真实的密码,比如只含小写字母,或者小写+大写+数字的组合?
Guess.Mask.......: ?l?l?l?l?l?l?l?l [8]
Time.Estimated...: Mon Sep 04 10:06:00 2017 (94 days, 10 hours)
Guess.Mask.......: ?1?1?1?1?1?1?1?1 [8] (-1 = ?l?u?d)
Time.Estimated...: Sun Aug 02 09:29:48 2020 (3 years, 61 days)
目前看来,像这样的蛮力攻击——拿字母或数字一个一个去试——在高端GPU上的表现也不算惊艳。但如果我们把数字除以8呢……手段是,在一台机器里装上8张显卡。这是小公司财力所能及的,或者有些有钱的个人也能做到。遗憾的是,38个月除以8得出的结果也并没有大幅降低攻击时间。别急,如果以举国之力来做这件事呢?他们财力雄厚,可以使用几千个这样的GPU(1.1天),甚至可能几万个GPU(2.7小时),然后呢……好了,即使密码的最低要求是10个字符,你在那种情况下也麻烦大了!
如果我们想让Discourse扛得住全国性的攻击,很显然,我们还需要努力。Hashcat有一个方便的基准模式,它在一台装有8张Nvidia GTX 1080 GPU的设备上测试,然后根据结果按顺序列出了最强(最慢)的哈希算法。从那份清单上,我看到了脱颖而出的bcrypt、scrypt和PBKDF2-HMAC-SHA512。
我用Hashcat的测试结果给了我一些信心,说明Discourse存入数据库的密码哈希并没有太偏离正确轨道。但是,我想要的是完全的确信,于是我请了一位有安全和穿透测试背景的人,来尝试破解当前托管在我们这儿的两个非常热门的Discourse站点的密码哈希(当然签了保密协议)。结果是这样的:
有人提供给我两套来自两个不同的Discourse社区的密码哈希,分别包含5909个和6088个哈希值。两者都使用了PBKDF2-HMAC-SHA256算法,并且工作因子是64k。我的机器配有Nvidia GTX 1080 Ti GPU,使用Hashcat计算哈希的速率大约是每秒27000次。
所有Discourse社区都共同遵循的密码规则如下:
利用上述密码规则制定出模板,我花了大概3周时间在11997个哈希里破解出了39个,其中25个是xxx社区的,14个是yyy社区的。
这是一位安全领域的研究员,他经常做如此这般的审计。因此,所有的攻击都使用了字典,并且根据他以前的经验总结出来的高效攻击模式——这不是真正意义上的蛮力攻击。他恢复出来的密码如下(其中有一个是重复的):
007007bond
123password
1qaz2wsx3e
A3eilm2s2y
Alexander12
alexander18
belladonna2
Charlie123
Chocolate1
christopher8
Elizabeth1
Enterprise01
Freedom123
greengrass123
hellothere01
I123456789
Iamawesome
khristopher
l1ghthouse l3tm3innow
Neversaynever
password1235
pittsburgh1
Playstation2
Playstation3
Qwerty1234
Qwertyuiop1
qwertyuiop1234567890
Spartan117
springfield0
Starcraft2
strawberry1
Summertime
Testing123
testing1234
thecakeisalie02
Thirteen13
Welcome12
如果我们把攻击力度提升到8倍,并且允许的时间翻倍,假设碰到一个打了鸡血的攻击者,或者他有一套很灵光的字典和模板,可以预见,他可能最后破解出 39 x 16 = 624个密码,也就是相当于所有用户中的5%。这是合理的估算,但仍然比我想象的要高。在Discourse的未来版本里,我们肯定会规划加入哈希类型表。这样我们就能在将来1~2年里,切换到一种更为安全(慢得多)的密码哈希策略。
bcrypt $2*$, Blowfish (Unix)
20273 H/s
scrypt
886.5 kH/s
PBKDF2-HMAC-SHA512
542.6 kH/s
PBKDF2-HMAC-SHA256
1646.7 kH/s
通过这次演练,我现在对我们最糟糕的安全场景有了一个更为深入的理解,也就是,数据库被盗走,然后在线下遭遇专业的密码哈希攻击。同时也让我更加自信地推荐和支持我们的开发工作,使得Discourse的用户人人都很安全。因此,如果你在安全实施方面并不完全确信(像我一样),是时候来验证一下那些假设啦。别等着黑客来攻击你——作为黑客,要勇于自黑!
相关阅读: