您现在的位置是:网站首页 > 敏感信息在前端的存储问题文章详情

敏感信息在前端的存储问题

敏感信息在前端的存储问题

前端开发中处理敏感信息时,存储方式的选择直接影响系统安全性。错误的存储方案可能导致数据泄露、会话劫持甚至法律合规风险。

常见敏感数据类型

需要特别关注的敏感数据类型包括但不限于:

  1. 用户身份凭证

    • 登录令牌(JWT、Session ID)
    • API密钥
    • OAuth令牌
  2. 个人隐私数据

    • 身份证号码
    • 银行卡信息
    • 医疗记录
  3. 业务敏感数据

    • 未公开的交易金额
    • 内部系统配置
    • 权限标识符

高风险存储方式及问题

localStorage的永久存储风险

// 危险示例:将JWT存储在localStorage
localStorage.setItem('auth_token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');

问题表现:

  • XSS攻击可完全读取所有存储内容
  • 无自动过期机制
  • 同源策略下所有页面共享

cookie的错误配置

// 不安全cookie设置
document.cookie = `session_id=3f8d7b2a1c; path=/`;

典型漏洞模式:

  • 缺少HttpOnly标志导致JS可读
  • 未设置Secure标志时通过HTTP传输
  • Domain过于宽松导致子域名共享

URL参数暴露

// 敏感数据通过URL传递
window.location.href = `/reset-password?token=${resetToken}`;

安全隐患:

  • 浏览器历史记录永久保存
  • 第三方扩展可能读取URL
  • 服务器日志完整记录

安全存储实践方案

会话管理最佳实践

// 安全cookie设置示例(需后端配合)
fetch('/login', {
  method: 'POST',
  credentials: 'include' // 确保cookie能被正确设置
});
// 后端应返回:Set-Cookie: sessionId=xxx; HttpOnly; Secure; SameSite=Strict

关键配置组合:

  • HttpOnly + Secure + SameSite
  • 短期过期时间(如30分钟)
  • 服务端签名验证

临时敏感数据处理

// 使用内存存储临时令牌
let temporaryToken = null;

function fetchToken() {
  return api.getToken().then(token => {
    temporaryToken = token; // 仅保存在内存中
    setTimeout(() => { temporaryToken = null }, 300000); // 5分钟后自动清除
  });
}

内存存储特点:

  • 页面刷新即丢失
  • 不受XSS持久化影响
  • 需配合自动清理机制

Web Crypto API的应用

// 使用SubtleCrypto进行客户端加密
async function encryptData(data, password) {
  const encoder = new TextEncoder();
  const keyMaterial = await crypto.subtle.importKey(
    'raw',
    encoder.encode(password),
    { name: 'PBKDF2' },
    false,
    ['deriveKey']
  );
  
  const salt = crypto.getRandomValues(new Uint8Array(16));
  const key = await crypto.subtle.deriveKey(
    { name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' },
    keyMaterial,
    { name: 'AES-GCM', length: 256 },
    false,
    ['encrypt', 'decrypt']
  );
  
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const encrypted = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    key,
    encoder.encode(data)
  );
  
  return { encrypted, iv, salt };
}

加密要点:

  • 使用强随机数生成IV和salt
  • 高迭代次数的密钥派生
  • 避免存储解密密钥

框架特定解决方案

React Context的安全用法

// 创建安全上下文
const AuthContext = React.createContext(null);

function AuthProvider({ children }) {
  const [token, setToken] = useState(null);
  
  // 自动清理机制
  useEffect(() => {
    const timer = setTimeout(() => setToken(null), 3600000);
    return () => clearTimeout(timer);
  }, [token]);

  return (
    <AuthContext.Provider value={{ token, setToken }}>
      {children}
    </AuthContext.Provider>
  );
}

// 使用示例
function Dashboard() {
  const { token } = useContext(AuthContext);
  // token仅存在于内存中
}

Vue响应式数据的保护

// 使用非响应式存储敏感数据
const secretStore = {
  _data: null,
  get secret() {
    return this._data;
  },
  set secret(value) {
    this._data = value;
    if (value) setTimeout(() => this._data = null, 1800000);
  }
}

// 在组件中使用
export default {
  methods: {
    storeToken(token) {
      secretStore.secret = token; // 不会触发Vue响应式系统
    }
  }
}

合规性要求考量

GDPR相关规范

  • 数据最小化原则:仅存储必要信息
  • 用户明确同意:对cookie存储的特殊要求
  • 数据主体权利:提供清除接口

PCI DSS支付标准

  • 禁止前端存储完整信用卡号
  • 允许显示的最后四位需脱敏处理
  • 必须使用符合标准的加密算法

监控与应急措施

异常访问检测

// 监听storage事件检测异常访问
window.addEventListener('storage', (event) => {
  if (event.key === 'auth_token' && event.oldValue !== event.newValue) {
    analytics.log('Suspicious token modification', {
      oldValue: event.oldValue?.slice(0, 5) + '...',
      newValue: event.newValue?.slice(0, 5) + '...'
    });
  }
});

数据泄露响应

  1. 立即使受影响令牌失效
  2. 强制用户重新认证
  3. 审计日志分析访问模式
  4. 根据法规要求进行通知

开发环境特殊处理

敏感配置隔离

// config.dev.js
export default {
  apiKey: 'dev_public_key'
};

// config.prod.js
export default {
  apiKey: process.env.API_KEY // 通过构建工具注入
};

// 实际使用
import config from './config.' + process.env.NODE_ENV;

注意事项:

  • 禁止将真实密钥提交到版本控制
  • 使用环境变量和.gitignore配合
  • 预提交钩子检查敏感信息

浏览器API的局限与补充

IndexedDB的安全增强

// 加密后存储到IndexedDB
async function secureStore(dbName, key, value, password) {
  const encrypted = await encryptData(value, password);
  const db = await indexedDB.open(dbName, 1);
  
  db.onupgradeneeded = (event) => {
    event.target.result.createObjectStore('secureStore');
  };
  
  return new Promise((resolve) => {
    db.onsuccess = (event) => {
      const tx = event.target.result.transaction('secureStore', 'readwrite');
      tx.objectStore('secureStore').put(encrypted, key);
      resolve();
    };
  });
}

补充方案:

  • 使用Web Workers处理加解密
  • 存储主密钥使用硬件安全模块(HSM)
  • 定期轮换加密密钥

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步