前端鉴权实战:Token 存哪、怎么刷新、怎么防 XSS 与 CSRF

前端鉴权实战:Token 存哪、怎么刷新、怎么防 XSS 与 CSRF

"localStorage 里存 token 有 XSS 风险,那放 cookie 就安全了"------错,CSRF 在等着。

前后端分离项目的鉴权方案,网上一搜全是互相矛盾的答案。这篇不讲理论推演,直接给结论和落地代码:Token 放哪、过期怎么无感刷新、XSS 和 CSRF 各自怎么防。这套方案我用在一个有支付功能的项目里,安全评审一次过。

一、先分清两个威胁,方案才不纠结

威胁 攻击者怎么拿/用你的凭证 防御核心
XSS 注入脚本在你的页面上下文里执行,能读 JS 能读的一切 CSP、不把 token 放 JS 可读的地方、输入输出转义
CSRF 恶意网站诱导浏览器自动携带 cookie 发请求(不需要读任何数据) Cookie SameSite、CSRF Token

记住这句就够:XSS 防的是"读走",CSRF 防的是"带上"。Token 放 localStorage 能防 CSRF(恶意站读不到),但挡不住 XSS;放 cookie 防得住"读走"(httpOnly),但要防 CSRF。没有银弹,是组合拳。

text 复制代码
Access Token(短期,15 分钟)  → 内存中持有,Authorization 头发送
Refresh Token(长期,7~30 天) → httpOnly + Secure + SameSite=Strict cookie

为什么这么分:

  • Access Token 放内存:刷新页面就没了,XSS 读到了也只有 15 分钟寿命,且不参与 CSRF(自定义请求头天然免疫,跨站表单带不上它)。
  • Refresh Token 放 httpOnly cookie :JS 读不到,XSS 偷不走;只发往 /auth/refresh 一个接口,攻击面被锁死;SameSite=Strict 让跨站请求根本不携带它,CSRF 无从谈起。

后端设置 refresh cookie 的关键属性(Express 示例):

js 复制代码
res.cookie("refresh_token", token, {
  httpOnly: true,                  // JS 不可读
  secure: true,                    // 仅 HTTPS
  sameSite: "strict",              // 跨站请求不携带
  path: "/auth/refresh",           // 只在这个路径发送,缩小暴露面
  maxAge: 7 * 24 * 3600 * 1000,
});

path 这一手经常被忽略:refresh token 只需要给刷新接口用,限定 path 后,其他任何请求(包括被 XSS 劫持的请求)都带不上它。

三、无感刷新:用户永远不该看到"请重新登录"

Access Token 15 分钟过期,如果用户被打断到登录页,产品体验就崩了。标准解法:请求拦截器发现 401 → 用 refresh token 换新 token → 重放原请求。Axios 实现:

ts 复制代码
let refreshing: Promise<string> | null = null;

api.interceptors.response.use(undefined, async (error) => {
  if (error.response?.status !== 401 || isRetry(error.config)) throw error;

  refreshing ??= refreshToken().finally(() => (refreshing = null)); // 并发只刷一次
  const newToken = await refreshing;                                 // 所有请求等同一个结果
  error.config.headers.Authorization = `Bearer ${newToken}`;
  error.config._retried = true;
  return api(error.config);                                          // 重放原请求
});

两个必须处理的细节:

  1. 并发去重 :页面一进来发 8 个请求,token 恰好过期,8 个 401 同时到。不去做并发合并,就会发起 8 次刷新、7 次因 refresh token 被轮换而失败、用户被登出。refreshing ??= ... 保证整页只有一个刷新请求,大家等同一个 Promise。
  2. 刷新也失败 :refresh token 过期/被吊销,此时清状态、跳登录 ,并把用户当前页面的 URL 存下来(sessionStorage.redirect = location.href),登录后跳回去。这个"登回原页面"的小细节,留存率差异很明显。

后端配套:refresh token 每次使用后轮换(旧的作废、发新的),并在数据库记录"设备会话"。同一个 refresh token 被第二次使用 = 可能被偷了,直接吊销整个会话。这是 OWASP 推荐的 refresh token rotation。

四、XSS 防御:鉴权方案只是兜底

Token 放内存只是降低损失,XSS 本身还要正面防:

  1. 不拼接 HTML 。React/Vue 默认转义插值,dangerouslySetInnerHTML/v-html 只允许出现在内容经过 DOMPurify 消毒的地方。
  2. CSP 收紧 :Content-Security-Policy: default-src 'self'; script-src 'self'。禁掉 inline script 和可疑外域,XSS 载荷大多数直接没法加载。注意用了 inline 事件或 JSON.pARSE 水合的要配 nonce。
  3. 依赖也是攻击面 :npm 投毒事件真实发生过,lock 文件提交、CI 用 npm ci、定期 npm audit。
  4. 别在 URL、日志里带 token:token 出现在 query string 会进各种访问日志。

五、CSRF:用对了 SameSite,传统 CSRF Token 可以省

CSRF 的成立条件是"浏览器自动携带凭证发跨站请求"。逐条拆解我们的方案:

  • 核心写接口用 Authorization 头 → 跨站表单/图片请求带不上,天然免疫。
  • refresh cookie 是 SameSite=Strict → 跨站请求根本不携带,免疫。
  • 如果站点必须兼容老浏览器、或 refresh cookie 用了 SameSite=Lax,再补一层 CSRF Token(后端下发随机值,写请求带头携带,服务端校验)。Lax 下跨站 POST 不带 cookie,但顶级导航的 GET 会带,所以GET 永远不要有副作用------这条比任何 token 都重要。

六、登出与"记住我":两个容易做错的点

登出不是删前端 token 就完了。后端必须吊销:access token 短命可以等它自然过期,refresh token 必须立即从会话表删除(或加黑名单)。否则"登出"只是前端表演,偷到 token 的人照用 7 天。

"记住我"的正确实现 是延长 refresh token 有效期(7 天 → 30 天),而不是延长 access token。见过把 access token 直接设 30 天的项目,等于把最容易被偷的凭证做成了长期凭证,前面所有努力白费。

七、总结

把整套方案压成一张决策表:

问题 答案
Access Token 放哪 内存,15 分钟,Authorization 头
Refresh Token 放哪 httpOnly + Secure + SameSite=Strict + 限定 path 的 cookie
过期怎么办 拦截器 401 → 并发合并的无感刷新 → 重放请求
refresh 怎么防偷 每次轮换 + 二次使用即吊销会话
XSS 输出转义 + CSP + 依赖审计(token 方案只是降损)
CSRF 自定义头免疫 + SameSite=Strict,GET 永远无副作用

按这张表把现有项目对一遍,重点检查三处:refresh token 是不是也在 localStorage 里(最常见的错)、401 并发刷新有没有合并、登出有没有吊销后端会话。

相关推荐
枫子有风1 天前
卡码网盘项目深挖
java·redis·jwt·es·pgvector
白帽攻防录1 天前
某pc客户端app从xss到rce实战案例
前端·网络安全·黑客·xss·rce·src漏洞·app漏洞
雨季mo浅忆1 天前
Vue关于前端XSS漏洞修复方案
前端·xss
文慧的科技江湖2 天前
Token工厂技术拆解(10月28日)|月底对账多付605块,账单上缓存那一栏一直是零
token·api网关·ai应用·大模型api·token中转站
FungLeo2 天前
成为全栈·Go 后端篇·跨语言认证兼容:Node 与 Go 如何互认 JWT 和密码
golang·go·jwt·身份认证·成为全栈·bcrypt
云上工程笔记3 天前
2026年大模型Token成本测算推荐清单:输入输出、缓存、并发与峰值用量
人工智能·spring boot·缓存·token
ly76896 天前
Spring Security 过滤器链定制:认证、CORS 与 CSRF 的先后顺序
java·spring boot·spring·spring security·csrf·cors·过滤器链
玩AI的奶茶7 天前
GPU 算力租赁平台横向对比:六家主流平台,24GB 档价差 85%
服务器·人工智能·gpu算力·token·算力租赁
江屿风8 天前
【Plain Language Large Model】【大语言模型底层原理全解析:从Token生成到智能体演进】流食般投喂
人工智能·笔记·语言模型·llm·json·token·聊天模板
跨境数据猎手8 天前
跨境电商系统三大 Web 漏洞的防护:SQL 注入、XSS、CSRF
前端·sql·xss