
IndexedDB 本身不加密,也不提供访问控制。所谓“安全存储”,本质是前端代码构建的一整套防护闭环:敏感数据必须加密后写入、密钥必须动态派生且绝不落地、结构设计要规避语义泄露、环境执行要隔离可信上下文。
敏感数据必须加密后存入,不能跳过这一步
密码、身份证号、银行卡号、短期 token 等高敏字段,严禁以明文形式进入 IndexedDB。同一源下的任意脚本(包括 XSS 注入代码、第三方 SDK)都能直接读取全部内容。低风险数据如主题设置、表单草稿、脱敏后的 API 缓存,才适合直接存储。
- 使用 Web Crypto API 的 AES-GCM(256 位),它同时保障机密性与完整性,拒绝 AES-CBC、XOR、Base64 等无认证的伪加密
- 每次加密都用
crypto.getRandomValues(new Uint8Array(12))生成全新 IV,和密文一起存为结构化对象,例如{ iv: 'base64', ciphertext: 'base64' } - 解密时必须校验 GCM 认证标签;失败即丢弃,不抛错、不打印、不返回任何提示
密钥不能存、不能记、只能按需派生
密钥是整个链条最脆弱的一环。浏览器中没有安全的持久化位置,策略是:只保存派生原料,不保存密钥本身。
- 主密钥来源必须绑定用户动作——输入密码、触发指纹/面容,或调用 WebAuthn 获取密钥句柄
- 用
deriveKey()配合 PBKDF2(≥10 万轮 SHA-256)或 HKDF 派生会话密钥;salt 应由服务端下发,防止彩虹表攻击 - CryptoKey 对象不可序列化,禁止
JSON.stringify()或存入 localStorage;作用域结束自动释放,不挂全局、不进 console - 用户登出或切换账号时,主动清理内存中所有密钥引用
数据库结构与命名要泛化、分仓、去语义
数据库名、objectStore 名、索引名在浏览器开发者工具中完全可见。攻击者可通过名称推测数据用途,因此架构上要主动降低信息泄露风险。
- 数据库名避免含业务关键词,例如不用
user_payment_db,可用core_data_v2或随机哈希值 - 按数据类型拆分为独立 objectStore:如
messages(正文)、metadata(时间/标题)、settings(偏好),便于按类隔离与清理 - 敏感字段单独加密,不整条记录打包加密——单个字段被篡改不影响其他字段可用性
配套措施缺一不可
加密只解决静态数据泄露问题,其他攻击面仍需覆盖:
- 强制 HTTPS 和 Secure Context,防止密码输入过程被中间人劫持
- 设置严格 CSP 策略,禁用内联脚本、限制未授权域名资源加载,压制 XSS 泄密能力
- 敏感操作(如密钥派生、解密)尽量放在独立 iframe 或弹窗中执行,隔离执行上下文
- 生产环境移除所有输出密钥、IV、明文的
console.log,禁用调试信息暴露

















