跨站请求伪造(CSRF)攻击与防御机制
什么是CSRF攻击?
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种利用用户已登录的身份,在用户无感知的情况下执行恶意操作的安全漏洞。攻击者通过构造特定的请求链接或嵌入恶意内容,诱使用户在目标网站上执行非预期的操作。
例如:用户在登录某社交平台后,收到一封邮件,点击其中的链接,该链接实际上会自动向平台发送一条"发表动态"的请求,而用户并未主动提交表单。
此类攻击无需用户点击按钮,甚至仅刷新页面即可触发,尤其在使用GET请求时传播范围更广,危害性极高。攻击形式多样,包括链接、图片、表单等,可被用于转账、发帖、修改密码等敏感操作。
攻击原理剖析
CSRF攻击依赖于两个核心条件:
- 用户已在目标站点完成认证,浏览器保存了身份凭证(如Cookie)。
- 攻击者诱导用户访问含有恶意请求的页面,该请求会携带用户的认证信息自动发送至目标服务器。
由于浏览器在发起请求时会自动附带当前域名下的所有有效Cookie,因此即使攻击者无法获取这些数据,也能借助用户的登录状态完成非法请求。
典型危害场景
- 未经授权的资金转移或消费
- 以用户名义发布不当内容,导致声誉受损
- 大规模传播恶意行为,形成类似网络蠕虫的效果
历史案例包括:某些平台因接口未校验来源,导致用户账户被批量扣费;社交媒体分享功能被利用,引发自动转发恶意消息的传播链。
防御策略详解
1. 利用 SameSite Cookie 属性
通过设置Cookie的 SameSite 属性,限制第三方上下文中的Cookie发送行为:
Strict:完全禁止跨站请求携带该Cookie,安全性最高。Lax:允许部分导航请求(如直接访问链接)携带Cookie,但不包含大多数跨站表单提交。None:允许跨站发送,但必须配合Secure属性(仅通过HTTPS传输)。
示例(Koa框架中设置):
ctx.cookies.set('sessionToken', token, {
sameSite: 'strict',
secure: true
});
注意:此方法依赖浏览器支持,部分旧版本浏览器可能不兼容。
2. 校验 Referer 头部信息
通过检查请求头中的 Referer 字段,判断请求是否来自可信源:
// PHP 示例
if (isset($_SERVER['HTTP_REFERER'])) {
$referer = $_SERVER['HTTP_REFERER'];
$allowedHosts = ['https://example.com', 'http://localhost'];
$isValid = false;
foreach ($allowedHosts as $host) {
if (strpos($referer, $host) === 0) {
$isValid = true;
break;
}
}
if (!$isValid) {
http_response_code(403);
exit('Forbidden: Invalid referer.');
}
}
注意:Referer 可能被伪造或禁用(如本地文件访问),需结合其他措施使用。
3. 使用一次性 Token 验证
最有效的防御手段之一。在页面加载时生成一个随机令牌(Token),并同时存储在表单和Cookie中:
<!-- 前端生成并嵌入表单 -->
<form method="POST" action="/submit">
<input type="hidden" name="csrf_token" value="abc123xyz">
<textarea name="content">Hello</textarea>
<button type="submit">提交</button>
</form>
<!-- 同时将 token 存入 cookie -->
<script>
document.cookie = "csrf_token=abc123xyz; path=/; SameSite=Lax";
</script>
后端在处理请求前验证表单中的Token与Cookie中的是否一致:
// 后端逻辑示例(Node.js + Express)
app.post('/submit', (req, res) => {
const formToken = req.body.csrf_token;
const cookieToken = req.cookies.csrf_token;
if (!formToken || !cookieToken || formToken !== cookieToken) {
return res.status(403).send('CSRF token mismatch');
}
// 正常处理业务逻辑
res.send('Success');
});
对于多标签页场景,可通过前端动态刷新Token或设置失效机制来解决冲突问题。
4. 图形验证码(辅助手段)
要求用户完成人机验证才能提交表单,可有效阻止自动化攻击。但由于影响用户体验,通常仅用于高风险操作(如支付、改密)。
推荐使用成熟的验证码库(如 Node.js 中的 ccap),但不建议作为通用防护方案。
PHP 实现示例
以下为在 PHP 中实现多种防御方式的代码片段:
① 设置 SameSite Cookie
header('Set-Cookie: auth_token=abc123; SameSite=Lax; Secure; Path=/');
② Referer 校验
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$allowed = ['https://yourdomain.com'];
if (!in_array($referer, $allowed)) {
die('Access denied');
}
③ Token 防护
<?php
$token = bin2hex(random_bytes(16));
setcookie('csrf_token', $token, [
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
];
?>
<form method="post">
<input type="hidden" name="csrf_token" value="">
<button type="submit">提交</button>
</form>
// 处理提交
if ($_POST['csrf_token'] !== $_COOKIE['csrf_token']) {
die('Invalid token');
}