现代crypto.subtle API需协同密钥管理、算法选择、数据封装和运行时防护:必须用AES-GCM(含12字节随机IV、16字节authTag及AAD),密钥不可硬编码或静态下发,须依敏感度分级派生;全站HTTPS、严格CSP、内存校验为必要前置防护;读写须封装为带协议头、版本校验与降级策略的原子化加密容器。

现代 crypto.subtle API 能提供工业级加密能力,但“工业级”不等于“开箱即用”。关键在于密钥管理、算法选择、数据封装和运行时防护四个环节的协同。单纯调用 encrypt() 并不能让 localStorage 变成保险柜——它只是工具,不是防线。
必须用 AES-GCM,且带完整认证标签
AES-GCM 是目前 Web Crypto 中唯一同时满足机密性、完整性与认证性的对称模式。它在加密时自动生成 16 字节认证标签(authTag),解密时自动校验,可有效防御密文篡改、重放或填充预言类攻击。
- 避免使用 AES-CBC:无认证机制,无法检测密文是否被恶意修改;
- IV 必须每次随机生成(
crypto.getRandomValues(new Uint8Array(12))),且长度严格为 12 字节(GCM 最佳实践); - 加密输出需完整保留:密文 + IV + authTag(三者缺一不可),推荐 Base64URL 编码后拼接为单字符串存储;
- 不要省略 AAD(附加认证数据):例如可传入当前域名或时间戳哈希,进一步绑定上下文,防跨域/跨会话复用。
密钥绝不能硬编码、也不能从后端静态下发
密钥是整套方案的命门。工业级场景下,密钥生命周期必须可控、可撤销、有上下文约束:
- 高敏数据(如短期 token、临时凭证):密钥应由用户密码 + 随机 salt(存于服务端)经 PBKDF2 或 HKDF 派生,且每次解密都需用户主动输入密码——适合密码管理器、金融确认页等强交互场景;
- 中低敏数据(如 UI 状态、混淆后的偏好设置):可在首次加载时用
generateKey('AES-GCM', true, ['encrypt','decrypt'])生成密钥,导出为 JWK 后加密保存至sessionStorage(仅本会话有效),避免跨页面泄露; - 禁用“后端下发固定密钥”:若服务端返回一个长期有效的
local_key,该密钥本身就成了新的攻击入口,需 HTTPS + 签名 + 时效性 + 一次性校验,复杂度远超直接走接口传数据。
写入前做三重基础加固,否则加密形同虚设
再强的加密,也挡不住脚本在内存中读取明文或 hook 解密函数。必须前置阻断常见攻击面:
- 强制全站 HTTPS:检查所有资源(
<script>、fetch、iframe)均为https://,防止中间人替换加密逻辑; - 启用严格 CSP:至少禁用
unsafe-inline、unsafe-eval和data:协议,限制内联脚本与动态执行,大幅压缩 XSS 利用空间; - 防内存窃取:在关键解密前加轻量运行时校验(如检查
window.crypto.subtle.decrypt.toString()是否被 Proxy 重写),或使用 Service Worker 拦截敏感读取请求(需配合可信上下文)。
读写封装要原子化、可审计、带错误隔离
不要裸写 localStorage.setItem,应封装为带类型、版本、过期时间的加密容器:
- 写入时:自动注入协议头(如
"v2:gcm:aes-256:")、时间戳、数据类型标识,便于后续升级与兼容; - 读取时:先校验协议头与版本号,再解析 IV/authTag/密文,任一环节失败立即丢弃并返回
null,不抛错、不暴露内部结构; - 支持降级策略:当 Web Crypto 不可用(如 Safari 15.6 以下),自动 fallback 到 CryptoJS AES-CBC(仅限低敏字段),并记录告警日志供安全审计;
- 所有加解密操作标记为
async,避免阻塞主线程;敏感字段建议额外加一层简单混淆(如字段名哈希映射),增加静态分析成本。


















