密码加密在前端开发中的正确姿势与常见误区
项目初期的技术选型
前段时间接了个项目,是个用户管理系统。客户特别强调了安全性,尤其是密码这块,不能存明文,得加密。其实这需求挺常见的,但当时我也没想太多,觉得无非就是加个哈希函数完事了。
最开始我打算用MD5,毕竟简单又好用,之前也用过很多次。然而在开发过程中才发现,MD5现在已经被认为不够安全了。客户那边的安全部门直接否了这个方案,说必须用更强的加密方式。于是我就开始研究其他的加密算法。
最大的坑:前端加密还是后端加密?
项目中遇到了一个挺纠结的问题:到底是在前端加密还是后端加密?我一开始想着干脆前后端都加,双重保险嘛。结果踩了个大坑——性能问题。
先说前端加密吧,我用了JavaScript的crypto.subtle库来实现AES加密:
async function encryptPassword(password) {
const encoder = new TextEncoder();
const data = encoder.encode(password);
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
data
);
return { encrypted, iv };
}
看起来没问题对吧?但实际运行时发现,每次用户输入密码都要花不少时间去加密,特别是低端设备上卡得厉害。而且更麻烦的是,前端加密后的数据传到后端还得解密再重新加密存储,这一来一回反而增加了复杂性。
最终的解决方案
后来调整了方案,决定只在后端做加密处理。用Node.js写了这么一段代码:
const crypto = require('crypto');
function hashPassword(password) {
return new Promise((resolve, reject) => {
const salt = crypto.randomBytes(16).toString('hex');
crypto.scrypt(password, salt, 64, (err, derivedKey) => {
if (err) reject(err);
resolve(salt + ':' + derivedKey.toString('hex'));
});
});
}
// 使用示例
hashPassword('user_password_123').then(hashed => {
console.log(hashed); // 存入数据库
});
这里用的是scrypt算法,相比bcrypt它的内存消耗更高,更能抵抗暴力破解。记得要设置合适的参数,不然性能开销会很大。
同时我还加了HTTPS,确保传输过程中的安全性。虽然HTTPS本身不加密密码,但它能防止中间人攻击,和后端加密配合起来效果不错。
踩坑提醒:盐值的管理
这里注意我踩过好几次坑:盐值(salt)一定要随机生成,并且每个用户都得不一样。开始没注意这点,用了固定盐值,结果被安全部门批了一顿。
另外盐值得和加密后的密码一起存进数据库,格式就用冒号分隔:
salt:hashed_password
验证密码的时候也要记得把盐值提取出来:
function verifyPassword(storedHash, password) {
return new Promise((resolve, reject) => {
const [salt, key] = storedHash.split(':');
crypto.scrypt(password, salt, 64, (err, derivedKey) => {
if (err) reject(err);
resolve(key === derivedKey.toString('hex'));
});
});
}
回顾与反思
整个项目下来,我觉得做得比较好的地方是最后采用了后端加密加HTTPS的方案,既保证了安全性又避免了性能问题。不过也有遗憾,比如前端用户体验那块,密码输入时的实时加密确实影响了响应速度,最后只能妥协去掉。
还有一个小问题是密码重置功能,目前是直接生成新密码发给用户邮箱。虽然用了临时密码机制,但总感觉还可以优化得更好些。
以上是我个人对这个密码加密项目的完整讲解,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

暂无评论