安全地存储用户密码
Storing User Passwords Securely: hashing, salting, and Bcrypt
在存储密码的数据库被攻破的前提下,破解密码的成本越高则密码的安全性就越高。 一般来说,密码在存储到数据库前都经过hash加密。 需要考虑hash函数的性能对密码安全性的影响。 比如对于SHA256来说,当前的普通计算机能在一秒内计算数百万的hash值,定制的计算机则可以提供每秒数十亿次的hash运算。 在这种情况下,很多密码安全的设计都变得不再安全。
首先,最不安全的方式是明文存储密码。 其次,对密码明文进行sha1加密、对密码明文进行sha1加密时加上固定的salt值、为每个用户分配不同的salt值都是不安全的。
推荐使用bcrypt加密密码。
bcrypt相比sha1等来说更慢,这意味着破解密码的cpu成本更高。
比如log_rounds
值设为10时,大概需要100毫秒来计算hash值。
还可以通过增加log_rounds
值来抵消未来计算机运算能力的提高。
一些bcrypt实现还原生支持per-user-salt。
Dropbox也主要采用了per-user-salt加bcrypt来存储用户密码。 Dropbox在用bcrypt hash之前,会先用SHA512对密码做预处理——把密码hash成512 bit的定长字符串。 预处理有两个作用:一是对于很短的密码,提高它的entropy(熵);二是防止输入密码过长造成潜在的DoS attacks风险(bcrypt很“慢”)。 Dropbox还引入了global pepper(既有“盐”又有“胡椒粉”)——对bcrypt的hash值再用AES256做进一步的加密。 pepper存储在数据库之外的地方。AES256是对称加密,方便未来更新pepper值。
为什么推荐用char[]而不是String来存储密码
Why is char[] preferred over String for passwords in Java?
为什么Java Swing用char[]
来存储密码,而不是String
?
因为String
是不可变的(immutable)。
这意味着在String
对象被垃圾回收前,密码会一直存在于内存中。
(虽然可以通过反射来修改String
对象的field的访问权限,进而修改String
对象。)
使用char[]
来存储密码的好处是使用后可以马上抹掉char[]
的内容。
安全总是相对的。一种可能性(取决于JVM实现)是在抹掉char[]
之前发生了GC,移动了char[]
对象
(比如从Eden移动到Survivor),使得老的char[]
还驻留在内存里。
还有一点要注意的是不要在log里(无意地)打印敏感信息,比如密码等。