文章目录
往期文章:
1 漏洞本质
CSRF 的本质是:攻击者利用浏览器对已认证用户的信任机制,在用户不知情时伪造合法请求执行非授权操作。
本质解读:
-
信任关系滥用
利用浏览器(不限于PC)自动发送凭证(Cookie)的机制 -
请求伪造
通过恶意页面构造合法格式的请求:<img src="https://xxx.com/abc?t=attacker&a=1000000">
-
无感知攻击
用户无需任何主动交互即可触发攻击
2 攻击原理
攻击条件
-
用户已登录存在 CSRF 漏洞的网站。
-
用户需要被诱导打开攻击者构造的恶意网站/恶意链接(可能是网址、图片、短信、音视频)。
3 风险场景
-
从用户行为角度解析:
通常认为 CSRF 攻击是攻击者利用受害者已登录状态诱导其点击链接。但从用户行为角度看,用户在多个标签页同时打开不同网站,且部分网站处于登录状态时,若在一个标签页中访问了恶意网站,该恶意网站可能利用其他标签页中已登录网站的 Cookie 等身份信息发起 CSRF 攻击。这意味着用户的多标签页浏览习惯在某些情况下会增加 CSRF 攻击风险。 -
与业务逻辑结合解析:
CSRF 漏洞不仅存在于常见的转账、修改密码等操作中,还可能与特定业务逻辑紧密相关。例如在一些项目管理系统中,若添加项目成员功能存在 CSRF 漏洞,攻击者可诱导管理员添加恶意成员,进而获取项目相关信息或权限,这种与特定业务结合的 CSRF 攻击往往更隐蔽,不易被常规安全检测发现。
本人工作中遇到的 CSRF 漏洞以第二种业务场景为主。
4 防御原则
“服务端必须验证每个状态变更请求的合法性,不能仅依赖浏览器自动提交的凭证”
- 前端框架职责:安全传输 CSRF Token
- 后端框架职责:严格验证请求来源和 Token 有效性
- 架构师职责:为敏感操作启用二次验证
5 官方框架
5.1 前端框架
5.1.1 Vue 安全指南
HTTP security vulnerabilities, such as cross-site request forgery (CSRF/XSRF) and cross-site script inclusion (XSSI), are primarily addressed on the backend, so they aren’t a concern of Vue’s. However, it’s still a good idea to communicate with your backend team to learn how to best interact with their API, e.g., by submitting CSRF tokens with form submissions.
Vue 对 CSRF 的基本立场
CSRF 属于后端负责解决的安全范畴,而非 Vue 框架的职责。Vue 在前端的安全防护中,更侧重于 XSS(跨站脚本攻击)等与 DOM 渲染相关的风险,而 CSRF 的防护需要前后端协同完成。
5.1.2 Angular 安全手册
默认实现
- 通过拦截器从名为
XSRF-TOKEN
的Cookie中读取令牌,并将其设置为请求头X-XSRF-TOKEN
。 - 仅对相对URL的变更请求(如POST)发送该头,GET/HEAD或绝对URL请求不发送。
工作流程
- 服务器在页面加载或首次GET请求时,向客户端设置
XSRF-TOKEN
Cookie(需包含唯一用户令牌)。 - 后续请求中,服务器验证Cookie与
X-XSRF-TOKEN
头是否一致,确保请求来自合法客户端。
自定义配置
若后端使用不同的 Cookie 或请求头名称,可通过HttpClientXsrfModule.withOptions()
配置:
// app.config.ts 示例
import { HttpClientXsrfModule } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
importProvidersFrom(HttpClientModule),
importProvidersFrom(
HttpClientXsrfModule.withOptions({
cookieName: 'My-Xsrf-Cookie', // 自定义Cookie名称
headerName: 'My-Xsrf-Header' // 自定义请求头名称
})
)
]
};
关键注意事项
- 服务器端配合:Angular 仅实现客户端防护,服务器必须设置 Cookie 并验证请求头,否则防护失效。
- 多应用隔离:同一域名下的多个 Angular 应用需设置唯一 Cookie 名称,避免令牌冲突。
5.1.3 React 官方文档
React 官方文档 中没有关于 CSRF 相关说明,因此官方默认还是由后端来看护。
5.2 后端框架
5.2.1 Spring Security (Java)
CSRF防护概述
Spring Security 默认对 POST 等不安全的 HTTP 方法提供 CSRF 防护,无需额外代码。其防护通过CsrfFilter
实现,该过滤器包CsrfTokenRepository
、CsrfTokenRequestHandler
等组件,处理流程包括判断请求是否需要 CSRF 防护、加载并验证 Token、处理访问拒绝异常等。
CsrfToken管理
- 存储方式
- 会话存储:默认使用HttpSessionCsrfTokenRepository将Token存储在会话中,可通过配置显式指定。
- Cookie存储:使用CookieCsrfTokenRepository将Token存储在Cookie中,默认Cookie名为XSRF-TOKEN,可配置HttpOnly属性,若JavaScript需要读取Cookie,需将HttpOnly设为false。
- 自定义存储(个人推荐):可实现CsrfTokenRepository接口,自定义Token存储位置。
5.2.2 Django (Python)
CSRF 防护基础配置
- 中间件启用:CSRF 中间件
django.middleware.csrf.CsrfViewMiddleware
默认激活,若覆盖MIDDLEWARE
配置,需确保该中间件排在处理视图中间件之前。 - 模板标签使用:在内部 URL 的 POST 表单模板中,需使用
{% csrf_token %}
标签,且视图需使用RequestContext
或render()
函数等以保证标签正常工作。
AJAX 请求的 CSRF 防护
- 获取令牌
- 当
CSRF_USE_SESSIONS
和CSRF_COOKIE_HTTPONLY
为False
时,从csrftoken
cookie 中获取令牌,可通过自定义getCookie
函数或 JavaScript Cookie 库获取。 - 当
CSRF_USE_SESSIONS
或CSRF_COOKIE_HTTPONLY
为True
时,在模板中通过{% csrf_token %}
生成令牌,再从 DOM 中读取。
- 当
- 设置令牌:在 AJAX 请求中,通过设置
X-CSRFToken
请求头传递令牌,如使用fetch()
时设置headers: {'X-CSRFToken': csrftoken}
,并确保mode: 'same-origin'
。
其他场景下的 CSRF 防护
- Jinja2 模板:Django 的 Jinja2 模板后端在上下文中添加
{{ csrf_input }}
,相当于 Django 模板语言中的{% csrf_token %}
。 - 装饰器使用:可使用
csrf_protect()
装饰器对特定视图进行保护,不建议单独使用,可与中间件同时使用。
5.2.3 Express.js (Node.js)
官方CSURF已经不再维护,该中间件存在大量安全漏洞报告,不建议继续使用。
官方文档推荐使用 npm csrf 模块。
5.3 框架防御机制对比
框架 | 防御方案 | 默认状态 | 关键配置 |
---|---|---|---|
Spring | 同步令牌模式 | 启用 | CsrfTokenRepository |
Django | 模板Token嵌入 | 启用 | CsrfViewMiddleware |
Express | 需手动集成 npm csrf 模块 | 无 | 参考 csrf 模块 |
React | 需手动集成 | 无 | axios拦截器附加Header |
Vue | 需手动集成 | 无 | 全局请求拦截器 |
Angular | HttpClientXsrfModule | 需手动配置 | withOptions()配置 |
6 防御措施
设置 Cookie 的各类安全配置虽能显著降低 CSRF(跨站请求伪造)风险,但无法完全防御。以下结合具体技术案例,从 Cookie 关键配置的防御效果、局限性及绕过方式展开分析:
6.1 Cookie核心安全配置的防御机制与案例
6.1.1 HttpOnly 属性:阻断前端JS获取Cookie
-
防御原理:设置
HttpOnly: true
后,Cookie仅能通过HTTP请求传输,前端JS(如document.cookie
)无法读取,防止XSS攻击间接利用 Cookie发起 CSRF。 -
技术案例:
若网站未启用 HttpOnly,攻击者可在恶意页面中通过JS读取用户登录Cookie(如session_id
),并构造伪造请求。例如:// 恶意页面JS代码 const cookie = document.cookie; fetch('https://xxx.com/transfer?a=1000&t=attacker', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: `cookie=${cookie}` });
启用 HttpOnly 后,
document.cookie
无法获取目标 Cookie,上述攻击失效。 -
局限性:
仅防御 XSS 间接利用 Cookie 的场景,无法阻止攻击者直接利用用户浏览器自动携带的 Cookie 发起 CSRF(如通过图片、表单等静默提交请求)。
6.1.2 SameSite 属性:限制跨站请求携带 Cookie
-
防御原理:通过
SameSite
属性控制Cookie是否在跨站请求中发送,可选值:Strict
:仅允许同站请求携带Cookie;Lax
:允许部分跨站请求(如GET方法的顶级导航)携带Cookie;None
:允许所有跨站请求携带Cookie(需配合Secure
属性)。
-
案例:
-
场景1:转账操作 CSRF
用户已登录银行网站a.com
,攻击者在b.com
构造转账表单:<form action="https://a.com/abc" method="post"> <input type="hidden" name="amount" value="1000"> <input type="hidden" name="to" value="attacker"> <button type="submit">点击领取福利</button> </form>
若银行网站 Cookie 设置
SameSite: Strict
,用户在b.com
提交表单时,浏览器不会携带a.com
的Cookie,攻击失败。 -
场景2:GET请求跨站导航
若Cookie设置SameSite: Lax
,用户点击b.com
的链接https://a.com/xxx?amount=1000&to=attacker
时,浏览器会携带Cookie(因属于GET方法的顶级导航),此时仍可能触发CSRF。
-
-
局限性:
Lax
模式无法防御GET请求的跨站导航攻击;None
模式需配合Secure
属性,但仍可能因用户浏览器兼容性问题(如旧版本浏览器不支持SameSite
)导致防御失效。
6.1.3 Secure属性:仅允许HTTPS传输Cookie
- 防御原理:设置
Secure: true
后,Cookie仅在HTTPS连接中传输,防止中间人攻击在HTTP信道中窃取Cookie。 - 技术案例:
若用户通过HTTP访问网站,攻击者可在公共 WiFi 环境中拦截请求,获取 Cookie 并伪造 CSRF 请求。启用 Secure 后,浏览器仅在HTTPS 连接下发送 Cookie,降低窃密风险。 - 局限性:
无法防御 HTTPS 环境下的 CSRF 攻击(如攻击者通过HTTPS站点发起跨站请求),仅能保障Cookie传输过程的安全性。
6.1.4 Domain / Path 属性:限制 Cookie 作用域
- 防御原理:
Domain
指定Cookie可被哪些域名访问(如Domain: example.com
限制仅example.com
及其子域名可使用);Path
指定Cookie可被哪些路径访问(如Path: /admin
限制仅example.com/admin
路径可使用)。
- 技术案例:
若网站a.example.com
的Cookie设置Domain: example.com
,则子域名b.example.com
可访问该Cookie,攻击者可能通过b.example.com
发起 CSRF。若正确设置Domain: a.example.com
,则可限制Cookie仅在当前子域名生效。 - 局限性:
若Domain设置过宽(如根域名example.com
),可能导致子域名漏洞被利用;Path配置错误也可能扩大Cookie作用域。
6.2 Cookie 配置无法完全防御 CSRF 的核心原因
6.2.1 浏览器自动携带 Cookie 的机制未被根本改变
- 即使启用
SameSite: Strict
,仍存在例外场景:- 用户在新标签页手动输入目标URL(如
https://a.com/transfer?amount=1000
),浏览器会携带Cookie,攻击者可通过社会工程学诱导用户执行; - 跨站POST请求可通过
iframe
+表单自动提交绕过SameSite
限制(需用户主动点击,但仍存在风险)。
- 用户在新标签页手动输入目标URL(如
6.2.2 与业务逻辑结合的 CSRF 漏洞无法仅靠 Cookie 防御
-
案例:某论坛允许用户通过GET请求修改头像(如
https://forum.com/change-avatar?url=恶意图片链接
),攻击者可构造图片链接:<img src="https://xxx.com/change-avatar?url=https://attacker.com/xxx.jpg">
若用户已登录论坛且Cookie未设置SameSite: Strict
,浏览器会携带Cookie发送请求,导致头像被篡改。此时需配合后端 CSRF 令牌(如Token)验证,而非仅依赖Cookie配置。
6.2.3 用户行为与浏览器兼容性引入的风险
- 用户可能禁用
SameSite
属性(如旧版浏览器默认不支持),或通过插件修改浏览器行为,导致Cookie配置失效; - 跨站请求若通过第三方资源(如 CDN、广告框架)发起,可能绕过部分Cookie限制。
6.3 完整防御 CSRF 的综合方案(Cookie 配置+其他措施)
6.3.1 Cookie配置最佳实践(推荐)
-
组合使用:
HttpOnly: true + SameSite: Strict + Secure: true + 精准Domain/Path
; -
示例:
Set-Cookie: session_id=12345; HttpOnly; SameSite=Strict; Secure; Domain=example.com; Path=/
6.3.2 后端 CSRF 令牌 Token 机制(推荐)
- 使用 token 的本质是增强请求参数的随机性 ,不被攻击者轻易猜测到请求的参数值,因此 token 的生成需要使用安全随机数。
客户端令牌 token 通常作为一种身份标识,由服务器端生成的一串字符串,当第一次登录后,服务器生成一个 token 返回给客户端,以后客户端只需带上 token 来请求数据即可,无需再次带上用户名和密码。
-
如果来自浏览器请求中的 token 值与服务器发送给用户的 token 不匹配,或者请求中 token 不存在,则拒绝该请求。
-
在《华为Web开发规范》中要求: “CSRF Token 存储在自定义请求头或集成框架的 session 中,推荐使用自定义请求头存储”。
6.3.3 Referer / Origin 头验证(辅助)
-
检查请求的
Referer
或Origin
字段是否来自可信域名。例如:# Django后端示例 def transfer(request): referer = request.META.get('HTTP_REFERER', '') if not referer.startswith('https://xxx.com'): return HttpResponseForbidden("非法请求") # 处理转账逻辑
-
局限性:
Referer
可被用户或代理服务器修改,仅作辅助防御。
6.4 防御结论
Cookie 的安全配置( HttpOnly / SameSite / Secure 等)是防御 CSRF 的重要环节,但无法单独实现完全防护。其核心原因在于:
- Cookie 配置仅能控制浏览器是否携带 Cookie,无法阻止用户主动发起的跨站请求;
- 业务逻辑层的漏洞(如GET请求执行敏感操作)需结合后端令牌机制防御;
- 浏览器兼容性与用户行为差异可能绕过部分Cookie限制。
最佳实践:以“Cookie 安全配置+后端 CSRF 令牌”为核心,辅以 Referer 验证、接口幂等性设计等多层防御,才能有效降低 CSRF 风险。
7 测试方法
7.1 Burp Suite(工具)
使用 Repeater 模块:
- 手动修改 / 删除 CSRF 令牌,测试服务器响应。
- 测试不同令牌值(空值、过期值),验证防御逻辑。
7.2 防御机制验证
检查敏感操作是否仅允许 POST 。
Cookie 安全属性测试
-
SameSite 属性:
用浏览器开发者工具检查 Cookie 是否设置SameSite=Strict。
在不同源页面中触发请求,观察 Cookie 是否被携带。
-
HttpOnly 属性:
用浏览器开发者工具检查 Cookie 是否勾选HttpOnly。
请求头验证
-
Referer 检查:
用工具修改请求的Referer头,验证服务器是否拒绝非法来源的请求。 -
Origin 检查:
对跨域请求,验证服务器是否验证Origin头与目标域名一致。
本文是「Web安全基础」系列的第 3 篇,点击专栏导航查看全部系列内容。