indexeddb 本身不加密,安全防护须由前端代码构建闭环链:写入前加密、读取后限时解密,用 web crypto api 的 aes-gcm(256 位)、随机 iv、结构化存储;密钥按需派生、不存储、不序列化;数据库名泛化、objectstore 分仓、字段级加密;环境侧信道防控包括禁用明文输出、iframe 隔离、csp 和 https 强制。

IndexedDB 本身不加密,所有安全防护必须由前端代码主动构建。真正的加密不是“加一层壳”,而是把密钥管理、算法选择、数据流转和环境风控全部串成一条闭环链。
加密必须在写入前完成,且解密只能在可信上下文中进行
每次向 IndexedDB 写入敏感字段(如聊天内容、密码、身份证号)前,必须先完成加密;读取后也必须立即解密并限时持有明文。不能把加密当作可选步骤,而应嵌入到所有数据库操作的主流程中:
- 使用 Web Crypto API 的 AES-GCM(256 位密钥),它同时提供机密性与完整性校验,避免用 AES-CBC 或简单 XOR 等无认证模式
- IV(初始化向量)必须每次加密时用
crypto.getRandomValues()生成 12 字节随机值,不可复用,也不可固定 - 密文与 IV 以结构化对象形式存储,例如
{ iv: 'base64...', ciphertext: 'base64...' },便于后续提取 - 解密失败(如认证标签不匹配)时,直接丢弃数据,不抛错、不打印、不返回任何提示信息
密钥不能存、不能记、不能传,只能“按需派生”
密钥是整套方案的命门。浏览器里没有绝对安全的存储位置,所以策略是:不保存密钥,只保存派生它的原料和方法。
- 主密钥来源必须绑定用户动作——比如输入密码、触发指纹/面容认证,或调用 WebAuthn 获取密钥句柄
- 用
deriveKey()配合 PBKDF2(10 万轮 SHA-256)或 HKDF 派生会话密钥,salt 必须唯一:服务端下发更稳妥;若本地生成,需用另一层密钥加密后存入 IndexedDB(形成密钥分层) - CryptoKey 对象不可序列化,禁止
JSON.stringify()或存入 localStorage;作用域结束即释放,不跨函数、不挂全局、不进 console - 用户登出或切换账号时,主动调用清理逻辑,确保内存中无残留密钥引用
结构设计要隔离、分仓、去语义,降低攻击面
IndexedDB 的数据库名、objectStore 名、索引名全程明文可见,攻击者可通过这些名称推测数据用途。因此架构上要规避语义泄露,并利用分仓提升可控性。
- 数据库名避免含敏感词,如不用
user_payment_db,可用泛化名如core_data_v2 - 按数据类型拆分为独立 objectStore,例如
messages(聊天正文)、metadata(对话时间/标题)、settings(用户偏好),删除某类数据时互不影响 - 敏感字段单独加密,不整条记录打包加密——单个字段损坏不影响其余字段解析
- 不依赖 IndexedDB 自身权限机制(它没有访问控制),所有鉴权逻辑前置到 JavaScript 层,比如解密前校验当前用户 session 是否有效
补齐环境侧信道与运维盲区
即使加密逻辑完美,错误的开发习惯或部署疏漏仍会导致密钥或明文意外暴露。
- 生产环境禁用所有输出明文、密钥、IV 的
console.log,CI 流程中加入关键词扫描(如 “key”、“iv”、“plaintext”) - 敏感操作(如密码输入、密钥解锁)放在独立 iframe 或弹窗中运行,隔离 DOM 和执行上下文
- 启用严格 CSP 策略,禁止内联脚本与 eval,防止 XSS 盗取运行时密钥
- HTTPS 强制启用,防止中间人劫持密码输入过程;Service Worker 不缓存加密相关 JS 资源











