前端用 SHA-256 加密用户密码真的安全吗?
我最近在做一个登录页面,想在前端用 SHA-256 对用户密码做哈希后再传给后端,但听说这样其实不安全?
我试了用 Web Crypto API 做哈希,代码大概长这样:
async function hashPassword(password) {
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
但同事说这根本防不住中间人攻击,而且等于把加密逻辑暴露给攻击者……到底该不该在前端做 SHA-256?
我之前项目也踩过这个坑,当时觉得前端加密了传输就安全了,结果被安全审计的同学一顿锤。
问题在哪呢?中间人攻击的情况下,攻击者不需要知道原始密码是什么,他只需要拿到你传的那个 SHA-256 哈希值,就能直接重放攻击——因为后端验证的就是这个哈希值。这就好比你换了个锁,但钥匙就挂在门上一样。
还有两个致命问题:一个是 SHA-256 算得太快,暴力破解成本极低;另一个是没加盐的话,同样的密码哈希值全世界都一樣,彩虹表分分钟给你安排上。
那正确的姿势是什么?
首先,密码传输必须走 HTTPS,这个是底线。只要 HTTPS 保证了,密码在传输过程中就是加密的,攻击者截获不到明文。
然后,后端存储的时候必须加盐,并且用慢哈希算法。bcrypt、scrypt、argon2 这些都行,它们设计上就是算得慢,防止暴力破解。
如果你非要较劲,说“我们就要在前端做点加密”,也不是不行,但得用非对称加密——后端给你公钥,你用公钥加密密码再传过去。这样中间人只能拿到密文,无法重放。不过说实话,既然有 HTTPS 了,这个操作意义不大,纯属增加复杂度。
所以结论就是:前端别搞 SHA-256 哈希了,老老实实走 HTTPS,后端用 bcrypt 之类的加盐慢哈希存密码,这才是正路。