"密码不能明文存储,要做哈希"——这条建议人人都听过,但很多人不知道的是,用普通的密码学哈希来存密码,其实远远不够。普通哈希为速度而生,而密码存储恰恰需要的是慢。再加上加盐、合适的成本参数等手段,才构成真正安全的密码存储方案。理解为什么密码需要"特殊对待",能帮你避开一个看似已经做对、实则漏洞百出的陷阱。

明文存储的灾难

先说最基本的:密码绝不能以明文存储。一旦数据库泄露,所有密码就直接暴露,攻击者不仅能登入你的系统,还能拿这些密码去尝试用户在其他网站的账号,因为很多人重复使用密码。明文存储把一次泄露放大成一场跨站的连锁灾难。

所以对密码做某种不可逆的处理是底线。但问题在于,仅仅"做了哈希"并不等于"做对了哈希"。

普通哈希的速度反成弱点

常见的密码学哈希算法设计得极快,每秒能处理海量数据——这在校验文件等场景里是优点。但用在密码上,这份速度恰恰帮了攻击者:拿到泄露的摘要后,他们可以每秒尝试天文数字般的候选密码,逐一计算哈希去比对,从而快速破解所有弱密码和常见密码。

换句话说,越快的哈希,越方便暴力破解。密码存储需要的,恰恰是一种故意算得很慢的哈希,让每一次尝试都付出可观的代价。

彩虹表与预计算攻击

如果所有用户的密码都用同样的方式直接哈希,攻击者还能预先计算好海量常见密码的摘要,存成一张巨大的查找表,破解时直接查表即可,连现场计算都省了。这种预计算攻击让弱密码几乎瞬间沦陷。

这正引出了加盐的必要性:必须让相同的密码在不同账户上产生不同的摘要,才能让预计算的查找表彻底失效。

加盐:让每个密码独一无二

盐是一段为每个密码单独生成的随机值,在哈希时和密码混合在一起。即便两个用户用了完全相同的密码,由于盐不同,他们存储的摘要也完全不同。这样一来,预先计算好的查找表就毫无用处,因为攻击者无法预知每个账户的盐。

盐不需要保密,它可以和摘要一起存储。它的作用不是隐藏,而是迫使攻击者必须针对每个账户单独破解,无法一次性攻破所有人。加盐把"一次预计算、处处通用"变成了"逐个账户硬啃"。

专用密码哈希函数

真正适合存密码的,是专门设计的密码哈希函数。它们内建了加盐机制,并且故意算得很慢;更进一步的方案还会刻意消耗大量内存,让攻击者难以用专用硬件大规模并行破解。这些设计的共同目标,是把每一次猜测的成本拉高到攻击者无法承受。

使用这类函数时,你通常不必自己操心加盐和迭代的细节,它们已经被妥善封装。选用一个被广泛认可的密码哈希方案,远比自己用普通哈希拼凑要安全得多。

成本参数与随时间调整

密码哈希函数通常带有一个可调的成本参数,用来控制它有多慢、消耗多少资源。设得越高,攻击者破解越费力,但你自己验证密码的开销也越大。这是一个需要权衡的旋钮:既要足够慢以抵御攻击,又不能慢到拖垮正常登录。

更重要的是,随着硬件越来越快,今天合适的成本参数,几年后可能就偏低了。安全的密码存储需要随时间提高成本参数,让破解的代价始终跟得上硬件的进步。这是一项需要持续维护的工作,而非一劳永逸。

把它当成一个完整的方案

归根结底,安全的密码存储不是"调用一次哈希函数"这么简单,而是一整套协同工作的措施:用专门的密码哈希函数,为每个密码加上独立的盐,设置足够高的成本参数,并随硬件进步持续调整。任何一环缺失,保护都会大打折扣。

好消息是,你几乎不需要自己从零实现这些。成熟的库和框架已经把正确的做法封装好了,你要做的是选用它们、配好参数,而不是用一个普通哈希草草了事。把密码哈希当成一个需要认真对待的完整方案,才能真正守住用户的凭据。