给用户的密码加点盐吧

阿衰最近的工作很是悠闲,之前的项目刚上线,新的任务还没下来。这天,他正趴在桌上发呆,想着早上开会老板说要招程序员鼓励师的事情。

“用户的密码都让你弄丢了,你还有心思睡大觉啊!”就在阿衰白日梦做得正美的时候,项目组长Bob站在了他的工位旁。

“用户登陆时输入的密码,你是如何传到服务器端的?”Bob厉声问道。

“老大,这回您一定是冤枉我了,”阿衰自信满满地回答道:“我当然有考虑过密码不能明文传输,所以是做了MD5加密才传的,数据库存的也是MD5加密后的值,丢密码这锅我可不背。”

“哎呦不错,长进了,知道用MD5了。那我问问你,知道MD5相较其他加密算法的优势在哪吗?”Bob继续发问。

“呃……这个……”阿衰吞吞吐吐答不上来。

“MD5不可逆嘛!”正在一旁敲代码的阿正凑了过来,他是阿衰的搭档,也是公司刚评选的年度优秀员工。

“严格来讲,MD5并不是一种加密算法,而是摘要算法的一种。这其中的区别在于,所谓加密,与之相对应的还有解密;然而摘要算法通过哈希(hash)运算后,无法重新复原原始数据。”阿正一本正经的说道。

看上去阿衰还是似懂非懂。不过此时的Bob似乎气消了很多,拿了把椅子坐了下来,看样子他也正想借这个机会和两位同事探讨一下这个问题。

“嗯,阿正说到点上了。一般而言,对于登陆系统的密码,我们都该存储其摘要信息,而非一个可解密的值。这样就算是马云,也不可能拿到我们支付宝的密码,因为根据数据库存的摘要信息根本无法还原出原始密码。”

作为Leader,Bob的举例不失幽默。他接着说:“实际上对于登陆时的密码认证而言,服务器端也没必要知道用户的原始密码。比如XXXX这个密码经过某种哈希算法运算后的结果是YYYY,注册时将YYYY存入数据库。下次用户登陆时输入XXXX,经过相同的哈希算法计算,还是会得到YYYY,将其和数据库中的记录做比对就可以了。”


“难怪之前在哪儿听说存密码要用MD5呢,原来摘要不等于加密啊!”阿衰总算是搞明白这其中的门道了,随即质问起来:“那这样说,我的处理没问题啊,用户的密码丢了与我何干?”

“不能解密并不意味着就绝对安全,”Bob似乎在等着阿衰的发难,他从容的解释道:“你看啊,由于哈希算法结果的唯一性,XXXX通过MD5计算后得到的值永远是YYYY,那么反过来,如果别人拿到YYYY这串字符,也就知道对应的明文是XXXX了……”(猿知原味注:理论上来讲,由于哈希碰撞的存在,YYYY对应的明文或许不止XXXX)

“等等老大,您这个逻辑也太牵强了吧,”很显然阿衰并不服气,他反驳道:“用户的密码都是自己设定的,并且经过MD5运算后得到的摘要有32位字符,我现在给您从数据库随便翻一条摘要记录,您能知道它对应的原始密码?”

“阿衰,不用翻了,看起来弄丢用户密码的确是咱们的过失,”阿正似乎明白了Bob的意思,他说道:“确实,仅通过MD5值要想知道一个特定用户设定的密码,似乎不那么容易。然而实际情况是很多网络安全意识不强的用户在设置密码时会设置一些很常见的密码,比如123456、abc123、admin888等,这些密码的MD5值对于黑客而言再熟悉不过了,只要这些弱口令的MD5值丢失了,还真就等于将原始密码完全暴露给别人了。”

“哈哈,还是阿正反应快,怎样阿衰,只是简单地用MD5保护用户的密码是不是靠不住啊?”

“啊,大意了大意了,我的锅我的锅。”阿衰这家伙工作能力虽说还有待提高,心态倒还蛮不错。

“那,老大,如何处理用户的密码更为安全呢?”阿正问道。

“首先说呢,阿衰用的MD5确实太古老了,虽然到目前还没有一种针对MD5的高效率攻击手段,但是为了安全起见,还是建议用一些复杂度更高的哈希算法,比如SHA256、SHA512、SHA-3、WHIRLPOOL等等,这样可以很大程度地提升攻击者暴力破解的困难度;其次,也是更重要的,在用哈希算法做密码校验的时候我们需要往里加点料。”Bob回答。

“加料?油盐酱醋,老大您说要哪样,我去买。”阿衰抖了个机灵。

“嘿!阿衰,被你小子说准了,我要谈的保护密码的方式俗称就叫做‘加盐’。”Bob似乎总是能猜到阿衰要说什么。他接着说:“如果有一位马大哈用户设置的密码是123456,我们可以在这串字符后边再拼接一段随机字符串,比如MADAHA,然后再对整个字符串去求hash(123456MADAHA)就安全多了,这里的MADAHA就被称为‘盐值’。”

“您的意思是在用户注册时,对其密码加盐并做哈希运算后存入数据库,之后做登陆验证时,只需要在计算哈希前对其输入的密码加入和之前相同的盐值就行了?”阿正问道。

“Binggo!就这个意思。这样一来即使有坏小子通过某种手段拿到了即使是弱口令的密文,也很难知道其明文,因为实际生产中我们给密码混入的盐值很长。”Bob又解释道。



“这个加盐真是一个好办法!老大,我现在就去改代码,放心吧,我一定想一个超级复杂的字符串作为盐值,用来拼接用户的密码……”此时的阿衰脑袋里已经不再是程序员鼓励师了。

“等等阿衰,你是打算对所有用户都用一个固定的盐值吗?”还没开工Bob已经不放心了。

“那不然呢?”阿衰不解。

Bob:“你看啊,如果你用一个固定的盐值,相同密码即便加盐,在数据库中的hash值仍然是一样的。如果有黑客攻破了数据库,虽然他无法立即看出哪些用户的密码是123456,但是他稍加整理就会发现有很多用户的hash值是相同的,换句话说,他也就知道了哪些用户使用了相同的密码……”

“原来如此啊!”又是阿正先反应了过来,“很多人同时都在使用的密码必定是弱密码,只需针对其中一人,多尝试一些弱口令去登陆,一定会有所斩获,如此一来这批使用相同弱口令的用户就被全锅端了。”

“嗯,还真有这种可能,那这么说对每个用户还得加不同的盐。”阿衰总是后知后觉,他接着说:“给每个用户生成不同的盐值倒是好实现,不过老大,这样一来盐值也要对应于每个用户存入数据库的,黑客如果攻破了数据库,不就把盐值暴露了吗?到时候黑客又可以遍历弱口令集合,依次给它们加上获取到的盐值再做hash运算,和存储的hash值做比对了。”

“哈哈,阿衰,这次你问了一个好问题,”Bob笑着说道:“你说的漏洞的确存在,不过如果让黑客通过你说的方式,挨个对每个用户去尝试弱口令登陆,这和他们直接在网站或者APP的登陆界面去做这件事有什么区别呢?记住,世界上原本就没有绝对的安全,只有相对安全的防御。之所以这次弄丢了用户密码的事我不打算责罚你,也是因为申诉密码被盗的用户设置的就是一个弱口令,从某种层面来讲,他对这件事该负主要责任。”



就在阿衰感动的无以言表的时候,一旁的阿正又开口了,他眉头紧锁,似乎仍有不解:“嗯……老大,刚才您说的我都懂了,不过有这样一个问题,加盐哈希究竟是该在服务器端做呢,还是在客户端做呢?我想了一下,如果在服务器端实现,密码还是要明文传过去;如果在客户端实现,登陆时首先要通过用户ID去服务器端拿其对应的盐值,这样就会多出一次请求……”

“多一次请求问题倒不大,但如果将加盐哈希这个动作放在客户端,传输的密文和数据库中存的就成了一个值。这样一来,假使有坏小子监听到了登录请求中的这段密文,即使在不知道用户原始密码的情况下,也可以伪造请求去模拟登陆。更进一步,一旦系统被脱库(猿知原味注:‘脱库’即通过非法手段获取到数据库数据),数据库里存的密文都可以被用来伪造登陆了,那所有用户的登录权限也就被坏小子拿到了。”

Bob顿了一下接着说:“所以只是在客户端实现‘加密’不是一个好主意,还是得放到服务器端去做。至于你说的密码明文传输的问题好解决,在客户端也对密码进行加盐哈希再传输不就得了,由于在后端还有一层防护,客户端的加盐不用考虑被黑客脱库后‘一锅端’的问题,可以加一个固定的盐,只是用来保证传输过程中的密码不是‘裸奔’就行了。”



“不对啊老大,”阿衰问道:“即便是客户端和服务器端都加密,如果有坏人监听到了传输的密文,哪怕无法破解原始密码还是可以伪造请求登陆啊。”

“嗯,是的,不过阿衰,这是另外一个层面的安全问题了,你可以使用HTTPS保证传输不被窃听,”Bob伸了个懒腰接着说道:“至少到目前为止,你基本上不用担心一夜之间用户的密码全都被盗了。”

“但是……”阿衰似乎还想问什么。

Bob挥了挥手打断了阿衰:“Web安全是个庞大的话题。今天咱就先讨论到这。楼下来了两位MM还等着面试程序员鼓励师呢,我先下去忙招聘了。”

“早说啊老大。我们都没问题了,这就去干活,您赶紧去吧!”阿衰还是那个阿衰。

随着新版本的发布,密码门事件总算是平息了。这天Bob召集开发组全员开会,他说道:“XXX项目进展的非常顺利,线上用户数正在呈指数级的增长。这样下去用不了多久,我们在阿里云上买的那一个RDS就要撑不住了。关于这点,阿正、阿衰,你们作为后端工程师,有什么看法……”

故事读完了,还是意犹未尽?没关系,关注“猿知原味”公众号(yz–yw),还有一大波生动有趣的干货等着你。

你可能感兴趣的:(给用户的密码加点盐吧)