localstorage 不加密,纯 ui 配置如主题、语言可明文存储;含敏感信息的配置须禁用 localstorage,改用 httponly cookie;确需加密时仅用 web crypto api 的 aes-gcm,密钥由口令派生或临时生成,iv 随机且 12 字节,数据 base64 编码后 json 存储。

LocalStorage 本身不加密,所有数据都是明文存储。用户个人配置是否需要加密,关键看它含不含敏感信息——比如深色模式、语言偏好、字体大小这类纯 UI 设置,明文存也无风险;但若混入 token、设备 ID、手机号等,哪怕加密也不该存在 localStorage 里。
先判断哪些配置真该存本地
真正适合存 localStorage 的个人配置,仅限用户主动设置、需跨会话保留、且不涉及身份或隐私的纯前端状态:
- 主题模式(light/dark/system)
- 界面语言(zh-CN/en-US)
- 表格列宽、折叠面板展开状态
- 自定义快捷键映射(不含系统级权限)
API 地址、灰度开关、调试标识这些,应由后端动态下发或构建时注入,不该由前端“自己记”。一旦配置项带身份上下文或长期有效凭证,就该交给 HttpOnly + Secure Cookie 管理。
必须加密时,用 Web Crypto API + AES-GCM
别用 crypto-js 或硬编码密钥。浏览器原生的 Web Crypto 是唯一可靠选择,且必须满足三项硬性要求:
- 算法固定为 AES-GCM:同时提供机密性和完整性校验,防篡改
- 密钥长度严格为 256 位(32 字节),导入时声明
length: 256 - IV 每次随机生成、长度固定为 12 字节,绝不复用、不写死
调用前务必检查环境:if (window.crypto && crypto.subtle),HTTP 页面会静默失败,HTTPS 是前提。
密钥不能落地,更不能进源码
前端没有安全的密钥存储位置。可行路径只有两条:
- 用户口令派生:登录后让用户输入主密码,用 PBKDF2 + 随机 salt(≥100,000 次迭代)生成密钥;salt 可和密文一起存 localStorage,主密码只在解密时临时输入
-
临时会话密钥:登录成功后调用
crypto.subtle.generateKey('AES-GCM', true, ['encrypt', 'decrypt'])创建一次性密钥,全程仅驻留内存,页面关闭即销毁
绝不能把密钥写死在 JS 里,也不能从接口直接返回未加密的密钥。
加密后结构化存储,不是直接塞 ArrayBuffer
Web Crypto 返回的是 ArrayBuffer,不能直接存 localStorage。正确做法是:
- 把 IV、密文、salt(如有) 打包成对象
- JSON 序列化:
{ iv: "base64", data: "base64", salt: "base64" } - Base64 编码推荐用
btoa(String.fromCharCode(...)),或更安全的 base64url(替换+和/,去掉填充=) - 解密时反向操作:JSON 解析 → Base64 解码 → 构造 Uint8Array → 传给
crypto.subtle.decrypt()
整个流程要闭环,漏掉 IV 或 salt 就无法还原原始数据。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











