在 Web 开发中,用户密码的安全存储是一项基础且至关重要的任务。然而,尽管安全知识不断普及,仍有许多开发者在初学 PHP 或构建简单系统时,习惯性地使用 md5() 函数对用户密码进行加密存储。今天我们就来聊聊,用 md5 存密码到底安不安全,以及这一做法背后存在哪些常见误区。
md5() 是 PHP 内置的一个哈希函数,它可以将任意长度的字符串转换成一个 32 字符长度的十六进制字符串。例如:
<span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-title function_ invoke__">md5</span></span><span>(</span><span><span class="hljs-string">'password123'</span></span><span>); </span><span><span class="hljs-comment">// 输出:482c811da5d5b4bc6d497ffa98491e38</span></span><span>
</span></span>
这是一个不可逆的散列过程(理论上)。早期许多开发者将其用于对用户密码进行“加密”存储,认为这样可以保护用户的隐私。
答案是:不安全,早已被淘汰。
主要原因有以下几点:
md5() 本意是用于校验数据完整性的哈希函数,它的计算速度极快。然而,正因为速度快,使得它非常容易被用于暴力破解攻击(brute force attack)或彩虹表攻击(rainbow table attack)。
攻击者可以在极短的时间内尝试大量组合来猜测出原始密码,特别是对于常见或弱密码几乎毫无抵抗能力。
md5() 是确定性函数——相同输入永远产生相同输出。这就导致如果两个用户的密码相同,其生成的哈希值也相同,攻击者只需破解一次,即可适用于多个账户。
而没有加盐的哈希存储方式,是非常容易被彩虹表击穿的。
互联网上存在大量 md5 哈希与明文密码的映射数据库,例如:你只需要将 md5('123456') 得到的哈希值粘贴进搜索引擎,往往立刻能查到原始密码。
PHP 从 5.5 版本起提供了 password_hash() 和 password_verify() 这两个函数,是目前推荐用于密码加密和验证的方式。
示例代码如下:
<span><span><span class="hljs-comment">// 生成密码哈希</span></span><span>
</span><span><span class="hljs-variable">$hash</span></span><span> = </span><span><span class="hljs-title function_ invoke__">password_hash</span></span><span>(</span><span><span class="hljs-string">'password123'</span></span><span>, PASSWORD_DEFAULT);
</span><span><span class="hljs-comment">// 验证密码</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">password_verify</span></span><span>(</span><span><span class="hljs-string">'password123'</span></span><span>, </span><span><span class="hljs-variable">$hash</span></span><span>)) {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">'密码正确'</span></span><span>;
} </span><span><span class="hljs-keyword">else</span></span><span> {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">'密码错误'</span></span><span>;
}
</span></span>
password_hash() 默认使用 bcrypt 算法,并自动生成随机盐值,内置抗击暴力破解和彩虹表攻击的机制。
自动加盐
可配置计算强度(成本因子)
可扩展(支持不同的算法,如 Argon2)
官方维护,符合现代密码存储标准
使用 md5() 存储密码是过时且危险的做法,虽然在早期项目中较为常见,但在今天已经不再安全。随着网络攻击手段的不断升级,开发者应摒弃陈旧的安全观念,使用更现代、更安全的方式来处理用户密码。
记住:安全不是靠“加密”,而是靠设计。
拥抱 password_hash(),拒绝 md5(),是对用户安全最基本的尊重。