前端用 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 比较高效,但这种单次哈希很容易被暴力破解。专业点说,前端加密更像是给用户一种虚假的安全感。
比较好的做法是完全在后端做密码处理,用专门设计的算法比如 bcrypt、argon2 这种带盐和多轮迭代的方案。后端处理的好处是你能把加密逻辑藏起来,而且可以自由调整安全性参数。
如果非要前端做点什么,建议只用 HTTPS 保证传输安全,然后让后端接管所有密码处理工作。简单来说,前端负责收集密码,后端负责安全存储和验证,这才是最省心又安全的做法。
顺便说下你的代码实现虽然没问题,但用在这种场景就是浪费精力,不如专注优化其他更有价值的部分。
我之前项目也踩过这个坑,当时觉得前端加密了传输就安全了,结果被安全审计的同学一顿锤。
问题在哪呢?中间人攻击的情况下,攻击者不需要知道原始密码是什么,他只需要拿到你传的那个 SHA-256 哈希值,就能直接重放攻击——因为后端验证的就是这个哈希值。这就好比你换了个锁,但钥匙就挂在门上一样。
还有两个致命问题:一个是 SHA-256 算得太快,暴力破解成本极低;另一个是没加盐的话,同样的密码哈希值全世界都一樣,彩虹表分分钟给你安排上。
那正确的姿势是什么?
首先,密码传输必须走 HTTPS,这个是底线。只要 HTTPS 保证了,密码在传输过程中就是加密的,攻击者截获不到明文。
然后,后端存储的时候必须加盐,并且用慢哈希算法。bcrypt、scrypt、argon2 这些都行,它们设计上就是算得慢,防止暴力破解。
如果你非要较劲,说“我们就要在前端做点加密”,也不是不行,但得用非对称加密——后端给你公钥,你用公钥加密密码再传过去。这样中间人只能拿到密文,无法重放。不过说实话,既然有 HTTPS 了,这个操作意义不大,纯属增加复杂度。
所以结论就是:前端别搞 SHA-256 哈希了,老老实实走 HTTPS,后端用 bcrypt 之类的加盐慢哈希存密码,这才是正路。