如何防止PHP中的跨站点伪造伪造(CSRF)攻击?
防止 PHP 中的跨站请求伪造(CSRF, Cross-Site Request Forgery)攻击,是保障 Web 应用安全性的重要措施之一。以下适用于你在使用 PHP 构建登录、表单、数据修改等功能时有效防御 CSRF 攻击。
什么是 CSRF 攻击?
CSRF 是一种利用用户登录状态,诱导其在不知情情况下执行恶意操作的攻击。
例如:
<img src="http://example.com/delete-user.php?id=123"> <!-- 用户已登录,被诱导点击 -->
防御 CSRF 的通用思路
核心原则是:验证每个敏感请求是否来自“可信页面”,主要方法:
- 使用 CSRF Token(推荐做法)
- 设置 Referer 验证(辅助防御)
- 使用 SameSite Cookie 属性
- 使用双重 Cookie 验证机制(更高级)
- 对表单/接口做白名单过滤
方法 1:使用 CSRF Token(最推荐)
每次生成一个随机的令牌(token),保存在用户 Session,并插入表单中。提交时进行验证。
1️⃣ 生成 CSRF Token(保存到 Session)
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
2️⃣ 表单中插入隐藏字段
<form method="POST" action="update-profile.php">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
<input type="text" name="name">
<input type="submit" value="更新">
</form>
3️⃣ 提交后验证 Token
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die("⚠️ CSRF 验证失败!");
}
// 安全处理业务逻辑
}
📌 hash_equals 可防止时序攻击。
方法 2:检查 HTTP Referer(辅助方法)
$referer = $_SERVER['HTTP_REFERER'] ?? '';
if (strpos($referer, 'https://yourdomain.com') !== 0) {
die("⚠️ 非法请求!");
}
⚠️ 缺点:部分浏览器可能不发送 Referer,或被篡改,因此 只能作为辅助。
方法 3:设置 SameSite Cookie 属性(现代浏览器支持)
在设置 session cookie 时添加 SameSite 限制:
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => 'yourdomain.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict' // 或 Lax
]);
session_start();
📌 Strict 最安全,但可能导致跨站跳转失败;Lax 较宽松。
方法 4:双重 Cookie 验证机制(Double Submit Cookie)
适用于 无 Session 的 REST API / SPA 场景。
- 服务器发送一个 CSRF Token Cookie
- 客户端每次请求时,在 header 或 body 中回传 token
- 服务器验证 cookie 与 body 中 token 一致
方法 5:使用 Web 框架内建 CSRF 防护
很多 PHP 框架已经默认支持:
| 框架 | 防护机制 |
|---|---|
| Laravel | 自动为所有 POST、PUT、PATCH、DELETE 请求验证 CSRF |
| Symfony | 使用 CSRF Token Manager |
| Yii2 | 自动开启表单 CSRF 验证 |
Laravel 示例:
<form method="POST" action="/profile">
@csrf
</form>
实战检测:常见 CSRF 攻击页面如何伪造请求
<form action="http://victim-site.com/update-password.php" method="POST">
<input type="hidden" name="password" value="123456">
<input type="submit" value="Click me">
</form>
✅ 防御成功的表现:服务器发现 CSRF Token 不匹配,直接拒绝请求。
📚 权威资料与文档链接
- OWASP CSRF 官方文档
- PHP.net session_set_cookie_params 文档
- Laravel CSRF Protection
- Symfony CSRF Protection
总结防御建议
| 方法 | 安全性 | 推荐等级 |
|---|---|---|
| CSRF Token + Session | ⭐⭐⭐⭐⭐ | ✅ 推荐 |
| CSRF Token + Double Cookie | ⭐⭐⭐⭐ | REST API 场景 |
| SameSite Cookie | ⭐⭐⭐ | 辅助 |
| Referer 验证 | ⭐⭐ | 不可靠,仅参考 |
更多详细内容,请关注其他相关文章!