html无法实现密钥轮换,必须由服务端控制下发新密钥、前端按kid加载并管理多版本密钥生命周期,禁用localstorage硬编码,改用indexeddb版本化存储。

HTML本身不参与密钥轮换,别在<script></script>里硬塞密钥切换逻辑
HTML是静态标记语言,没有运行时能力,也无法感知密钥有效期或触发加密操作。所谓“HTML实现密钥轮换”,本质是前端JS调用加密库(如Web Crypto API)配合后端策略完成的过渡行为。把密钥逻辑塞进HTML文件(比如写死在<script></script>里或data-key属性中),只会让旧密钥长期暴露、无法及时失效。
- 密钥轮换必须由服务端控制下发时机,前端只负责按指令加载新密钥、停用旧密钥
- HTML中唯一可做的,是预留安全的密钥加载钩子,比如一个带版本号的
<script id="crypto-loader" data-version="2"></script> - 直接在HTML里写
const KEY_V1 = "xxx"或用localStorage存密钥,等于把轮换机制降级为手动改代码
Web Crypto API中importKey()支持多版本密钥共存,但需手动管理生命周期
浏览器原生加密能力(Web Crypto API)允许同时导入多个密钥,但不会自动识别“哪个是新密钥”“哪个该停用”。你得靠自己维护密钥映射表,并在解密前判断当前数据用的是哪版密钥。
- 密钥本身无元数据,
importKey()返回的CryptoKey对象不带版本、过期时间等字段 - 推荐做法:服务端在加密数据时附带
kid(key ID),前端根据kid查本地缓存的对应密钥,找不到再触发拉取 - 不要复用同一个
keyUsages数组混用加/解密场景——比如["encrypt", "decrypt"]用于长期密钥,而["decrypt"]专用于只解密的旧密钥,避免误用
旧密钥解密失败常见错误:不是InvalidAccessError,而是DataError或静默失败
当用错密钥解密时,Web Crypto API通常不抛出明确的“密钥不匹配”错误,而是返回DataError(内容损坏)或直接返回ArrayBuffer但解码为乱码——这会让前端误判为网络问题或数据异常,而非密钥版本问题。
- 典型现象:新密钥上线后,老用户打开历史消息全变空或报
DOMException: DataError,但控制台没提示具体哪步出错 - 关键排查点:检查
decrypt()调用前是否传入了正确的algorithm参数,比如AES-GCM的iv长度是否与加密时一致(旧密钥可能用12字节,新密钥强制16字节) - 务必在
catch块里打印完整错误:console.error("Decrypt failed:", e.name, e.message),DataError和InvalidStateError语义完全不同
过渡期必须禁用localStorage硬编码密钥,改用IndexedDB + 版本化存储
用localStorage.setItem("enc_key", key)存密钥,会导致轮换时无法区分密钥来源、无法原子替换、且无法设置过期时间。一旦旧密钥被覆盖,所有依赖它的历史数据就永久不可解。
-
IndexedDB支持事务和键路径,可建库crypto_keys,每条记录含kid、keyData、createdAt、deprecatedAt - 解密前先查
deprecatedAt是否为空或早于当前时间,是则跳过该密钥 - 服务端推送新密钥时,前端应以
put()写入新记录,而非clear()整个库——保留旧密钥直到确认所有历史数据已迁移完毕
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











