jol*_*olt 2 encryption passwords hash
我知道,我知道,类似的问题已经被问到了数百万和数十亿次,但由于大多数人都有不同的味道,我得到了自己的一个.
目前我正在开发一个旨在全国各地推出的网站,因此需要对用户系统进行某种保护.
我最近一直在阅读很多关于密码加密,散列,腌制......你说出来的名字,但在阅读了很多文章之后,我感到很困惑.
有人说简单的SHA512加密对于密码是足够的,其他人说无论你做什么都必须使用"盐",然后有人说你应该建立一个全新的密码加密机器,因为这样没有人能够得到它.
现在我正在使用hash_hmac();
SHA512,加密码获取随机SHA1盐和最后一部分,定义随机md5(); 键.对于我们大多数人来说,它听起来很安全,但是它呢?
我最近在这里看到,bcrypt();
(现在称为crypt();
Blowfish哈希)是最安全的方式.在阅读了关于crypt();
和相关内容的PHP手册之后,我很困惑.
基本上,问题是,我是否会hash_hmac();
击败Blowfished crypt();
,反之亦然?
还有一个,也许有更安全的密码散列选项?
Tho*_*nin 10
正确应用密码术的关键是以足够的精度定义您所追求的属性.
通常,当有人想要哈希密码时,它在以下上下文中:服务器正在验证用户; 用户通过保密渠道(HTTPS ...)显示其密码.因此,服务器必须存储用户密码,或者至少存储可用于验证密码的东西.我们不希望"按原样"存储密码,因为获得对服务器数据库的读访问权的攻击者将学习所有密码.这是我们的攻击模型.
一个密码是一些东西,适合于普通用户的大脑,因此它不能完全不可猜测.一些用户将选择具有高熵的非常长的密码,但是大多数将选择具有不高于例如32位的熵的密码.这是一种说法,攻击者在找到合适的密码之前必须"尝试"平均少于2 31(约20亿)的潜在密码.
无论服务器存储什么,验证密码就足够了; 因此,我们的攻击者拥有尝试密码所需的所有数据,仅限于他可以集中的计算能力.这称为离线字典攻击.
必须假设我们的攻击者可以破解一个密码.那时我们可能希望有两个属性:
这两个属性需要可以组合的不同对策.
1.缓慢哈希
散列函数很快.计算能力很便宜.作为数据点,使用SHA-1作为散列函数,以及130美元的NVidia图形卡,我每秒可以散列1.6亿个密码.2 31的费用在大约13秒内支付.因此SHA-1的安全性太快.
另一方面,用户将看不到在1μs中进行身份验证和在1ms内进行身份验证之间的任何差异.所以这里的技巧是以一种使其缓慢的方式扭曲哈希函数.
例如,给定散列函数H,使用定义为的另一个散列函数H':
H'(x)= H(x || x || x || ... || x)
在哪里' || '意味着连接.简单来说,重复输入足够的次数,以便计算H'函数需要一些不可忽略的时间.因此,您设置时间目标,例如1ms,并调整达到该目标所需的重复次数.10ms意味着您的服务器将能够以每秒10%的计算能力为代价来验证每秒10个用户.请注意,我们所讨论的是一个存储散列密码的服务器,因为它本身不可用,因此这里没有互操作性问题:每个服务器都可以使用特定的重复计数,并根据其功率进行定制.
现在假设攻击者可以拥有100倍的计算能力; 例如,攻击者是一个无聊的学生 - 许多安全系统的克星 - 并且可以在他的大学校园里使用数十台计算机.此外,攻击者可能会使用散列函数H的更全面优化的实现(您正在谈论PHP,但攻击者可以进行汇编).此外,攻击者是耐心的:用户不能等待超过几分之一秒,但是一个足够无聊的学生可能会尝试几天.然而,尝试20亿个密码仍需要大约3整天的计算时间.这不是最终安全的,但在单个便宜的PC上要好于13秒.
2.盐
一个盐是一块,你为了防止密码哈希公共数据的共享.
当攻击者可以将哈希努力重用于多个受攻击的密码时,就会发生"共享".当攻击者有几个哈希密码(他读取整个哈希密码数据库)时会发生这种情况:每当他发现一个潜在密码时,他就可以查看他试图攻击的所有哈希密码.我们称之为并行字典攻击.另一个共享实例是攻击者可以构建一个预先计算的散列密码表,然后重复使用他的表(通过简单的查找).传说中的彩虹表只是一个预计算表的特殊情况(这只是一个时间内存权衡,它允许使用比硬盘上更大的预计算表;但是构建表仍然需要散列每个潜在密码).时空方面,并行攻击和预先计算的表是相同的攻击.
腌制失败分享.salt是一个公共数据元素,它改变了散列过程(可以说salt 在一组不同的函数中选择散列函数).盐的意思是它对每个密码都是唯一的.攻击者不能再共享破解工作,因为任何预先计算的表都必须使用特定的盐,并且对于使用不同盐进行哈希处理的密码无效.
必须使用salt来验证密码,因此服务器必须为每个散列密码存储用于散列密码的salt值.在数据库中,这只是一个额外的列.或者你可以在一个blob中连接salt和hash密码; 这只是数据编码的问题,取决于你.
假设S是盐(即一些字节),密码p的哈希过程是:H'(S || p)(在前一节中定义了H'函数).而已!
对于每个散列密码,盐的点是尽可能唯一的.实现这一目标的一种简单方法是使用随机盐:无论何时创建或更改密码,使用随机生成器获取16个随机字节.16个字节应该足以使盐重用非常不可能.请注意,对于每个密码,salt应该是唯一的:使用用户名作为salt是不够的(某些不同的服务器实例可能具有相同名称的用户 - 存在多少"bob"? - 以及,有些用户更改了密码,新密码不应使用与之前密码相同的盐.
3.选择哈希函数
该H"散列函数是建立在一个散列函数^ h.一些传统的实现使用加密算法扭曲成哈希函数(例如,用于Unix的DES crypt()
).这促进了"加密密码"表达的使用,虽然它不合适(密码没有加密,因为没有解密过程;正确的术语是"哈希密码").但是,使用专为散列目的而设计的实际散列函数似乎更安全.
最常用的散列函数是:MD5,SHA-1,SHA-256,SHA-512(后两者统称为"SHA-2").在MD5和SHA-1中发现了一些缺点.这些弱点对某些用法产生了严重影响,但并非如此对于上面所描述的(弱点是关于碰撞,而我们在这里工作的前像素抗性).但是,选择SHA-256或SHA-512是更好的公共关系:如果你使用MD5或SHA-1,你可能需要证明自己是正确的.SHA-256和SHA-512的输出大小和性能不同(在某些系统上,SHA-256比SHA-512快得多,而在其他系统上,SHA-512比SHA-256快).但是,性能在这里不是问题(无论散列函数的内部速度如何,我们通过输入重复都会使速度慢得多),并且256位SHA-256输出绰绰有余.为了节省存储成本,将哈希函数输出截断为前n位是加密有效的,只要保持至少128位(n> = 128).
4.结论
无论何时创建或修改密码,都要生成一个新的随机盐S(16个字节).然后将密码p哈希为SHA-256(S || p || S || p || S || p || ... || S || p)其中' S || p '模式重复到足够时间到散列过程需要10ms.存储S和散列结果.要验证用户密码,请检索S,重新计算哈希值,并将其与存储的值进行比较.
而且你会活得更久,更快乐.