敏感数据禁用 localstorage 明文存储,应采用 web crypto api 的 aes-gcm 模式加密,密钥动态生成或口令派生,iv 每次随机且 12 字节,加密数据结构化 base64 编码存储,原始密码、jwt、身份证号等高敏信息根本不该存于前端。

直接用 localStorage.setItem 存敏感数据等于把钥匙挂在门把手上——明文存储本身就不安全。真正提升安全等级,关键在三件事:选对算法、管住密钥、规范存取结构。
必须用 AES-GCM 模式加密
别碰 crypto-js 或手动实现 CBC/ECB。Web Crypto API 的 AesGcm 是目前浏览器原生最稳妥的选择,它一次性完成加密和完整性校验,避免 IV 和 MAC 拼接出错。
- 密钥长度固定为 256 位(32 字节),导入时显式声明
length: 256 - IV 必须每次随机生成,且严格为 12 字节(GCM 推荐值),绝不可复用或硬编码
- 调用前检查环境:
if (window.crypto && crypto.subtle),HTTP 页面会静默失败
密钥不能落地,只能动态生成或派生
前端没有安全的“保险柜”,所以密钥本身绝不能出现在 localStorage、URL、cookie 或源码里。
- 用户口令派生:用
PBKDF2+ 随机 salt + ≥100,000 次迭代,salt 可和密文一起存,主口令只在解密时由用户输入 - 临时会话密钥:登录后调用
crypto.subtle.generateKey('AES-GCM', true, ['encrypt', 'decrypt']),密钥全程只存在内存变量中,页面关闭即销毁 - 禁止写死密钥、从接口明文返回密钥、或用 localStorage 存密钥字符串
加密后必须结构化 + Base64 编码
Web Crypto 返回的是 ArrayBuffer,不能直接塞进 localStorage。必须转成可序列化的格式,并保留必要元信息。
- 把 IV、密文、salt(如有)打包为对象:
{ iv: "base64", data: "base64", salt: "base64" } - 推荐用 base64url 编码(替换
+为-,/为_,去掉=填充),比btoa更健壮 - 存储前统一
JSON.stringify();读取后先JSON.parse(),再 Base64 解码为 Uint8Array,最后传给decrypt()
哪些数据根本不该存
加密是补救手段,不是免责金牌。以下内容即使加密也应避免本地存储:
- 原始密码、JWT token、API 密钥——交给后端管理,用
HttpOnly + Secure Cookie - 手机号、身份证号、银行卡号——前端不该持有,服务端按需返回脱敏结果
- 长期有效的访问令牌——必须设过期时间,配合服务端黑名单机制











