存储用户密码应该使用什么加密算法?
Bcrypt
简介
bcrypt 是基于 eksblowfish 算法设计的加密哈希函数,它最大的特点是:可以动态调整工作因子(迭代次数)来调整计算速度,因此就算以后计算机能力不断增加,它仍然可以抵抗暴力攻击。
关于eksblowfish算法,它是采用分组加密模式,并且支持动态设定密钥计算成本(迭代次数)。算法的详细介绍可查看下面文章:
http://t.cn/A66nuubF
结构
bcrypt 函数输入的密码字符串不超过 72 个字节、包含算法标识符、一个计算成本和一个 16 字节(128 位)的盐值。通过输入计算得到 24字节(192位)哈希,最终输出格式如下:
$2a$12$DQoa2eT/aXFPgIoGwfllHuj4wEA3F71WWT7E/Trez331HGDUSRvXi\__/\/ \____________________/\_____________________________/Alg Cost Salt Hash
$2a$: bcrypt 算法标识符或叫版本;
12: 工作因子 (2^12 表示 4096 次迭代)
DQoa2eT/aXFPgIoGwfllHu: base64 的盐值;
j4wEA3F71WWT7E/Trez331HGDUSRvXi: 计算后的 Base64 哈希值(24 字节)。
bcrypt 版本
$2a$: 规定哈希字符串必须是 UTF-8 编码,必须包含空终止符。
$2y$: 该版本为修复 2011年6月 PHP 在 bcrypt 实现中的一个错误。
$2b$: 该版本为修复 2014年2月 OpenBSD 在 bcrypt 实现中的一个错误。
2014年2月 在 OpenBSD 的 bcrypt 实现中发现,它使用一个无符号的 8 位值来保存密码的长度。对于长度超过255字节的密码,密码将在72或长度模256 中的较小者处被截断,而不是被截断为72字节。例如:260 字节的密码将被截断为4个字节,而不是截断为 72 个字节。
实践
bcrypt 关键在于设定合适的工作因子,理想的工作因子没有特定的法则,主要还是取决于服务器的性能和应用程序上的用户数量,一般在安全性和应用性能之间权衡设定。
我们使用 spring security BCryptPasswordEncoder 看下不同因子下产生哈希的时间,我电脑配置如下:
处理器:2.2 GHz 四核Intel Core i7
内存:16 GB 1600 MHz DDR3
显卡:Intel Iris Pro 1536 MB
Map
bcrypt | cost: 8, time : 39msbcrypt | cost: 9, time : 45msbcrypt | cost: 10, time : 89msbcrypt | cost: 11, time : 195msbcrypt | cost: 12, time : 376msbcrypt | cost: 13, time : 720msbcrypt | cost: 14, time : 1430msbcrypt | cost: 15, time : 2809msbcrypt | cost: 16, time : 5351msbcrypt | cost: 17, time : 10737msbcrypt | cost: 18, time : 21417msbcrypt | cost: 19, time : 43789msbcrypt | cost: 20, time : 88723msbcrypt | cost: 21, time : 176704ms
拟合得到以下公式:
10.3064 \cdot e^{0.696464 x}
10.3064e
0.696464x
BCryptPasswordEncoder 因子范围在 4-31 ,默认是 10,我们根据公式推导一下 31时需要多长时间。
/** * @param strength the log rounds to use, between 4 and 31 */ public BCryptPasswordEncoder(int strength) { this(strength, null); }
10.3064 \cdot e^{0.696464(31)} = 24529665567.08815
10.3064e
0.696464(31)
=24529665567.08815
工作因子 31 时大概需要 284 天,所以我们知道使用 bcrypt 可以很容易的扩展哈希计算过程以适应更快的硬件,为我们留出很大的回旋余地,以防止攻击者从未来的技术改进中受益。
SCrypt
SCrypt 比上面提到的算法出来较晚,是Colin Percival于 2009 年 3 月创建的基于密码的密钥派生函数。
Spring security 也实现该算法 。
浮生若梦
