知识分享平台项目复习第二天:登录模块中的双令牌与刷新安全(2)

项目复习第二天:从 Token 存储风险理解 XSS、CSRF 与 CORS

复习日期:2026-09-14

当前主线:认证安全------登录闭环

一、为什么登录模块会遇到 XSS、CSRF 和 CORS

第一天复习登录模块时,我已经理解了 Access Token、Refresh Token、Redis jti 白名单、Rotation 和登出空窗期。继续讨论前端令牌存储时,又出现了一个新的问题:当前项目把 Access Token 和 Refresh Token 都保存在 localStorage 中,这样虽然实现简单,但需要考虑浏览器环境中的安全风险。

当前项目的前端会从 localStorage 读取 Access Token,并主动加入请求头:

复制代码
Authorization: Bearer <access-token>

这让我需要区分三个经常同时出现、但职责完全不同的概念:

  • XSS:攻击者的代码进入目标网站并执行。

  • CSRF:攻击者借助浏览器自动携带的登录凭证执行操作。

  • CORS:浏览器控制跨源页面能否读取接口响应。

它们都与浏览器安全有关,但 XSS 和 CSRF 是攻击方式,CORS 是浏览器的跨源访问控制机制。

二、XSS:攻击者的代码进入了目标网站

XSS 全称是 Cross-Site Scripting,即跨站脚本攻击。

它的核心不是"攻击者从外部调用了接口",而是攻击者设法让恶意 JavaScript 在知享页面中运行。代码虽然由攻击者编写,但浏览器会把它当成知享网站自身的代码。

例如,如果一个恶意脚本能够在当前页面中执行,它可能读取:

复制代码
localStorage.getItem("zhiguang_auth_tokens")

当前项目把 Access Token 和 Refresh Token 都存放在这个位置。因此一旦发生 XSS,恶意脚本可能读取双令牌,将它们发送给攻击者,或者直接调用知享接口冒充当前用户。

XSS 脚本还可能:

  • 修改页面显示内容;

  • 读取用户在页面中输入的信息;

  • 调用发布、修改资料等业务接口;

  • 读取非 HttpOnly Cookie;

  • 窃取页面中其他可以被 JavaScript 访问的数据。

React 默认会转义普通文本,但这不代表 React 项目天然不会出现 XSS。不安全的 HTML 渲染、DOM 操作、第三方脚本或其他注入入口仍然可能产生风险。

三、CSRF:攻击者借用了浏览器自动携带的身份

CSRF 全称是 Cross-Site Request Forgery,即跨站请求伪造。

它通常与 Cookie 认证相关,因为 Cookie 可能由浏览器自动携带。攻击者不一定能读取用户的 Cookie,但可以诱导用户浏览器向目标网站发送请求。

典型过程是:

复制代码
用户已经登录知享,浏览器保存了登录 Cookie
→ 用户访问 evil.com
→ evil.com 诱导浏览器向知享发送修改资料或注销请求
→ 浏览器可能自动携带知享的 Cookie
→ 后端误以为这是用户主动执行的操作

CSRF 的目标通常是让请求产生副作用。攻击者不一定需要看到响应内容,只要修改资料、退出登录或其他敏感操作已经执行,攻击目的就可能达到。

当前项目主要使用 localStorage + Authorization。攻击者网站不能直接读取知享域名下的 localStorage,也就不知道用户的 Access Token,无法主动构造正确的 Authorization 请求头。因此,这种方式不像 Cookie 认证那样容易受到传统 CSRF,但主要风险会转向 XSS 窃取 Token。

四、CORS:控制跨源页面能否读取响应

CORS 全称是 Cross-Origin Resource Sharing,即跨源资源共享。

一个源由三部分组成:

复制代码
源 = 协议 + 域名 + 端口

只要其中一项不同,就属于不同源。例如:

复制代码
https://zhiguang.example
https://evil.example

以及:

复制代码
http://localhost:5173
http://localhost:8080

浏览器默认通过同源策略限制一个页面读取其他源的数据。CORS 是服务器通过响应头告诉浏览器,哪些来源的 JavaScript 可以读取自己的响应。

CORS 不是:

  • 用户认证机制;

  • 接口授权机制;

  • 服务端防火墙;

  • XSS 防护;

  • 完整的 CSRF 防护。

CORS 主要限制攻击者页面是否能够读取响应,但不能保证所有跨源请求都不会发送。某些请求可能已经携带 Cookie 到达后端并产生副作用,只是浏览器最终不允许攻击者页面读取响应。

curl、Postman 和服务端程序也不受浏览器 CORS 规则限制,因为 CORS 是浏览器执行的安全机制。

五、一次跨源判断的误区

考虑这样一个场景:恶意代码运行在 evil.com,它读不到知享的 localStorage,但用户浏览器会自动携带知享的登录 Cookie。攻击者诱导浏览器向知享发送修改资料请求。

这个场景属于 CSRF。但我最开始认为:"这是用户本地浏览器发送的请求,所以没有跨源。"

这个判断是错误的。请求是不是跨源,不取决于它是不是由用户本地浏览器发送,而取决于发起请求的页面源与目标接口源是否相同。

这里的关系是:

复制代码
发起页面源:evil.com
目标接口源:zhiguang.example

两个源不同,所以请求仍然是跨源请求。

纠正后的理解是:

CORS 主要决定攻击者页面能否读取知享接口的响应,但请求可能已经携带 Cookie 到达后端。即使攻击者读不到响应,退出登录、修改资料等副作用也可能已经产生。

六、HttpOnly 为什么只能降低 XSS 危害

当 Refresh Token 已经保存在 HttpOnly Cookie 中,但知享页面仍然发生 XSS 时,恶意 JavaScript 不能直接读取和复制 Refresh Token,但仍然可以以当前用户身份调用接口,在被攻击的页面中发送请求并冒充用户执行操作。

这说明 HttpOnly 解决的是 Cookie 内容被 JavaScript 直接读取的问题,而不是阻止页面中的恶意脚本执行操作。

HttpOnly 保护的是 Cookie 内容的机密性:JavaScript 不能通过 document.cookie 直接读取和复制 Refresh Token。但是浏览器仍然可以在符合条件的请求中自动携带 Cookie。因此,XSS 脚本虽然难以把 Refresh Token 复制到攻击者自己的设备,却仍然可以在当前页面中调用修改资料、发布内容或刷新 Token 等接口。

所以:

HttpOnly 可以降低 Refresh Token 被 XSS 直接窃取的风险,但不能阻止恶意脚本在当前登录页面中操作用户账号。

七、XSS、CSRF 与 CORS 的最终区分

概念 攻击代码的位置 利用的条件 主要风险
XSS 目标网站页面内部 网站执行了恶意脚本 读取 Token、修改页面、冒充用户
CSRF 攻击者网站 浏览器自动携带 Cookie 借用户身份执行有副作用的操作
CORS 不是攻击类型 浏览器的跨源读取规则 决定跨源 JavaScript 能否读取响应

可以用下面三句话快速记忆:

  • XSS 是攻击者已经混进了目标网站。

  • CSRF 是攻击者借用了用户浏览器携带的身份。

  • CORS 是浏览器控制外部页面能否读取目标网站的响应。

对于当前项目:

复制代码
双令牌存入 localStorage
→ 主要关注 XSS 读取 Token

Refresh Token 改为 HttpOnly Cookie
→ 降低 Refresh Token 被直接读取的风险
→ 但需要额外关注 CSRF

八、Spring Security 认证链路

回到登录闭环后,当前对 Access Token 如何经过 Spring Security 到达 Controller 的理解如下:

目前已经确认的内容是:

  • Access Token 由 JwtDecoder 验签。

  • permitAll() 不等于跳过整个安全过滤器链。

  • 401 表示请求没有完成认证,例如 Token 缺失、过期、格式错误或签名错误。

  • 403 表示认证已经成功,但当前用户权限不足。

目前对Spring Security 认证完整链路的理解不完整,以下是Access Token 经过 Spring Security 到达 Controller 的完整流程:

复制代码
请求携带 Authorization: Bearer <Access Token>
→ 进入 SecurityFilterChain
→ Spring Security 提取 Bearer Token
→ JwtDecoder 校验 Token
→ 校验成功后创建 Authentication
→ 将 Authentication 放入 SecurityContext
→ 执行授权判断
→ 通过后执行 Controller

九、第二天复习结果

已达到基础复述要求

  • XSS 的执行位置以及它对 localStorage Token 的风险。

  • CSRF 利用浏览器自动携带 Cookie 产生副作用。

  • CORS 主要控制跨源响应读取,不能替代 CSRF 防护。

  • HttpOnly 能降低 Refresh Token 被直接读取的风险,但不能彻底阻止 XSS 操作用户账号。

  • 401 与 403 的基本区别。

  • permitAll() 不等于跳过整个安全过滤器链。

相关推荐
Seraphina362 天前
DVWA(XSS Reflected/Stored High,CSRF Low)
前端·xss·csrf
cpolar技术支持2 天前
Spring Boot 接口本地正常,异地前端却报跨域?用 cpolar 跑通 CORS 预检与白名单
java·springboot·cpolar·前后端分离·cors
Seraphina363 天前
DVWA(SQL Injection-High,XSS Reflected-Medium,XSS Stored-Medium)
数据库·经验分享·sql·网络安全·xss
gudufy3 天前
Blazor 安全设计:一个 Blazor Admin 后台需要防哪些攻击?
csrf·ssrf·越权·easyadminblazor·blazor安全
其实防守也摸鱼3 天前
网安自测题:掌握核心知识点的实用练习
linux·运维·服务器·前端·数据库·sql·xss
Seraphina364 天前
DVWA(SQL注入-low,medium,XSS反射-low)
前端·数据库·笔记·sql·网络安全·web·xss
FungLeo4 天前
成为全栈·Next.js 网站前台篇·同源 BFF 代理:API、附件、Cookie 与跨站写入保护
react·csrf·next.js·bff·成为全栈·route handler
cindershade5 天前
登录态不是一枚令牌,而是一条可撤销的生命线
xss
szial5 天前
网络爬虫与 CDP 实战(二):浏览器能访问,Python 却被拒绝?拆开登录态与 CSRF
爬虫·python·csrf
hasty9 天前
HTML 已经转义,为何仍有 XSS?Sharp srcdoc 漏洞中的第二次解析
前端·html·xss