CSRF(Cross-Site Request Forgery)跨站请求伪造,是一种常见的Web安全威胁。简单来说,就是攻击者诱导用户在已登录的Web应用中执行非预期的操作。这种攻击利用了Web应用对用户浏览器的信任机制。
想象一下这样的场景:你登录了网上银行,然后不小心点击了一个恶意链接。这个链接可能悄悄地向银行服务器发送转账请求,而银行服务器会认为这是你主动发起的合法请求。这就是典型的CSRF攻击。
CSRF攻击通常具有以下特征:
一个完整的CSRF攻击通常包含以下步骤:
假设有一个银行网站的转账接口如下:
code复制POST /transfer HTTP/1.1
Host: bank.example.com
Content-Type: application/x-www-form-urlencoded
amount=1000&to=attacker
攻击者可以在自己的网站上放置这样的HTML代码:
html复制<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="amount" value="1000">
<input type="hidden" name="to" value="attacker">
</form>
<script>document.forms[0].submit();</script>
当已登录银行网站的用户访问这个恶意页面时,转账请求会自动发送,而用户可能完全不知情。
最有效的CSRF防御方法是使用CSRF令牌(CSRF Token)。其核心思想是:
实现示例(PHP):
php复制// 生成令牌
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
// 在表单中嵌入令牌
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
// 验证令牌
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('CSRF token validation failed');
}
现代浏览器支持SameSite Cookie属性,可以有效防御CSRF攻击:
php复制// 设置SameSite属性
session_set_cookie_params([
'samesite' => 'Strict',
'secure' => true,
'httponly' => true
]);
SameSite有三种模式:
为了更好地理解CSRF攻击,我们可以搭建一个简单的测试环境:
php复制// login.php
session_start();
$_SESSION['user'] = 'victim';
setcookie('sessionid', 'victim_session', ['samesite' => 'None', 'secure' => true]);
// transfer.php
session_start();
if ($_SESSION['user'] !== 'victim') die('未登录');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
file_put_contents('transfers.log',
"Transfer to {$_POST['to']}: {$_POST['amount']}\n",
FILE_APPEND);
echo "转账成功";
}
html复制<!-- evil.html -->
<form action="https://target-site.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>
传统CSRF防御通常针对表单提交,但现代应用常使用JSON API。攻击者可能利用以下方式发起攻击:
html复制<script>
fetch('https://api.example.com/transfer', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'text/plain'},
body: '{"to":"attacker","amount":1000}'
});
</script>
防御方法:
正确配置CORS可以防止某些CSRF攻击:
php复制// 正确的CORS配置
header('Access-Control-Allow-Origin: https://trusted-site.com');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET, POST');
错误配置(危险):
php复制header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Credentials: true');
根据应用场景选择合适的防御组合:
| 场景 | 推荐防御措施 |
|---|---|
| 传统表单应用 | CSRF令牌 + SameSite Cookie |
| SPA应用 | CSRF令牌 + CORS严格限制 |
| API服务 | CSRF令牌 + 严格Content-Type检查 |
| 高安全需求 | 多因素认证 + 操作确认 |
主流Web框架都内置了CSRF防护:
python复制# settings.py
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'Strict'
# 模板中
<form method="post">{% csrf_token %} ... </form>
java复制@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
javascript复制const csrf = require('csurf');
const cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(csrf({ cookie: true }));
// 在视图中
<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
...
</form>
检测网站是否存在CSRF漏洞的步骤:
常用的CSRF漏洞扫描工具:
使用示例(ZAP):
某社交网站曾存在CSRF漏洞,允许攻击者:
漏洞原因:
修复方案:
移动应用同样面临CSRF风险:
防护建议:
虽然都是Web安全威胁,但存在重要区别:
| 特性 | CSRF | XSS |
|---|---|---|
| 目标 | 利用用户权限执行操作 | 窃取用户数据或会话 |
| 执行环境 | 受害者的浏览器 | 受害者的浏览器 |
| 防御重点 | 请求验证 | 输入输出过滤 |
点击劫持(Clickjacking)是另一种界面伪装攻击:
php复制header('X-Frame-Options: DENY');
将CSRF防护纳入开发流程:
定期:
我在实际项目中发现,很多CSRF漏洞源于开发人员对"同源策略"的误解。浏览器允许跨域请求携带Cookie,但限制读取响应,这是CSRF能够成功的关键。理解这一点,就能更好地设计防护措施。