PHP CSRF防护全攻略:从原理到实战
一、什么是CSRF攻击?
跨站请求伪造(Cross-Site Request Forgery,简称CSRF或XSRF)是一种常见的Web安全漏洞,攻击者通过诱导用户在已认证的状态下执行非预期操作(如转账、删除数据、修改密码等)。其核心原理是利用用户浏览器自动携带的Cookie凭证,伪造合法用户的请求。
二、CSRF攻击的原理与危害
2.1 攻击流程
- 用户已登录:用户访问并登录目标网站(如银行、电商平台),浏览器自动保存Cookie。
- 攻击者构造恶意请求:攻击者在钓鱼页面或第三方网站中嵌入隐藏表单/链接,伪造指向目标网站的敏感操作接口(如
/transfer.php)。 - 用户触发请求:用户在不知情的情况下点击恶意链接或提交表单,浏览器自动携带Cookie发送请求。
- 完成非预期操作:目标网站因验证通过(仅检查Cookie),误认为是用户主动操作,执行攻击者伪造的请求。
2.2 典型场景
- 转账攻击:攻击者在钓鱼页面放一个隐藏表单,提交到银行转账接口,用户点击后完成转账。
- 删除数据:伪造GET请求(如
/delete.php?id=1),用户点击后直接删除数据。 - 修改密码:诱导用户提交包含新密码的表单,攻击者通过伪造请求修改密码。
三、PHP应用中的常见CSRF漏洞
3.1 未验证Token的表单提交
// 漏洞示例:修改个人信息的表单(无Token验证)
<form action="/update_profile.php" method="post">
<input type="text" name="email" value="user@example.com">
<input type="submit" value="更新">
</form>
攻击者可伪造请求提交恶意邮箱,用户点击后直接覆盖原邮箱。
3.2 使用GET请求执行敏感操作
// 漏洞示例:删除用户数据的GET请求
<a href="/delete_user.php?id=123">删除账号</a>
攻击者构造链接(如钓鱼页面中的隐藏<a>标签),用户点击后直接执行删除。
四、PHP CSRF防护核心方案
4.1 CSRF Token验证(最推荐)
原理:在用户会话中生成唯一Token,存储在Session或Cookie中,同时在请求中携带Token参数。服务器验证Token一致性,不一致则拒绝请求。
实现步骤:
- 生成Token:在用户访问页面时,生成随机Token并存储到Session。
- 前端渲染Token:将Token注入表单隐藏字段。
- 后端验证Token:提交请求时,检查Session中的Token与请求参数中的Token是否一致。
代码示例:
// 1. 生成Token(Session存储)
session_start();
$csrf_token = bin2hex(random_bytes(32)); // 生成32字节随机字符串
$_SESSION['csrf_token'] = $csrf_token;
// 2. 前端表单(HTML)
<form action="/update_profile.php" method="post">
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token; ?>">
<input type="text" name="email" value="user@example.com">
<input type="submit" value="更新">
</form>
// 3. 后端验证(PHP)
session_start();
$submitted_token = $_POST['csrf_token'] ?? '';
$stored_token = $_SESSION['csrf_token'] ?? '';
if ($submitted_token !== $stored_token) {
die("CSRF Token验证失败");
}
// 验证通过,执行敏感操作
适用场景:所有敏感操作(修改、删除、转账等)的POST请求。
4.2 SameSite Cookie属性(辅助防护)
原理:通过设置Cookie的SameSite属性,限制浏览器在跨域请求中自动携带Cookie,仅允许同域请求携带。
实现方式:
- PHP设置Cookie:
// 设置SameSite=Strict(仅同域请求携带Cookie) setcookie('PHPSESSID', session_id(), [ 'samesite' => 'Strict', 'secure' => true, // HTTPS下使用 'httponly' => true, // 防止JS读取 'path' => '/' ]); - Nginx/Apache配置:
# Nginx配置(全局设置Cookie属性) add_header Set-Cookie "SameSite=Strict; Secure; HttpOnly; Path=/";
注意:SameSite=Strict可能影响第三方登录(如OAuth),可使用Lax(允许GET请求跨域携带)。
4.3 验证Referer/Origin头
原理:检查HTTP请求头中的Referer(完整URL)或Origin(仅域名),确认请求来源是否合法。
代码示例:
// 后端验证Referer
$allowed_referers = ['https://yourdomain.com', 'https://admin.yourdomain.com'];
$referer = $_SERVER['HTTP_REFERER'] ?? '';
// 检查是否包含合法域名(允许部分子域名)
if (!in_array(parse_url($referer, PHP_URL_HOST), $allowed_referers)) {
die("非法请求来源");
}
局限性:Referer可被伪造,且HTTPS跳转HTTP时可能丢失Referer。
4.4 双重提交Cookie
原理:将CSRF Token同时存储在Cookie和请求参数中,服务器验证两者是否一致。
实现步骤:
- 生成Token:存储在Cookie中(
HttpOnly防止XSS读取)。 - 前端提交Token:在请求参数中携带Token。
- 后端验证:检查Cookie中的Token与请求参数中的Token是否一致。
代码示例:
// 1. 生成Token并写入Cookie
setcookie('csrf_cookie', $csrf_token, [
'samesite' => 'Lax',
'httponly' => true,
'path' => '/'
]);
// 2. 前端表单(参数携带Token)
<input type="hidden" name="csrf_cookie" value="<?php echo $csrf_token; ?>">
// 3. 后端验证
$cookie_token = $_COOKIE['csrf_cookie'] ?? '';
$param_token = $_POST['csrf_cookie'] ?? '';
if ($cookie_token !== $param_token) {
die("CSRF验证失败");
}
4.5 业务逻辑增强(辅助手段)
原理:结合敏感操作的额外验证(如密码、验证码、短信验证),即使Token被绕过,也能阻止恶意操作。
示例:
// 修改密码时需原密码验证
if ($_POST['old_password'] !== $user['password'] ||
$_POST['new_password'] !== $_POST['confirm_password']) {
die("密码验证失败");
}
五、PHP框架内置防护(如Laravel、Symfony)
5.1 Laravel框架
Laravel通过CSRF中间件自动验证Token,无需手动实现:
- 生成Token:表单中使用
@csrf指令自动渲染隐藏字段。 - 后端验证:
VerifyCsrfToken中间件自动检查Session与请求参数中的Token。
5.2 Symfony框架
Symfony通过Csrf组件实现Token生成与验证:
use Symfony\Component\Security\Csrf\CsrfTokenManager;
$tokenManager = new CsrfTokenManager();
$token
