CSRF 攻击的基本原理
CSRF(Cross-Site Request Forgery)攻击是一种利用用户已登录的身份,在用户不知情的情况下执行非预期操作的攻击方式。攻击者通过诱导用户访问恶意页面或点击链接,触发对目标站点的请求,从而以用户权限完成非法操作。
CSRF 攻击的核心机制
CSRF 攻击依赖于浏览器自动携带用户凭证(如 Cookie)的机制。当用户登录某个网站后,浏览器会存储会话 Cookie,并在后续请求中自动附加这些 Cookie。攻击者利用这一特性,构造一个恶意请求,诱使用户在已登录状态下触发该请求。
关键条件包括:
- 用户已登录目标站点并保持会话
- 目标站点未采用有效的 CSRF 防护措施
- 用户被诱导执行了攻击者预设的操作
典型攻击场景分析
银行转账案例
假设某银行网站的转账接口如下:
POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: sessionid=user123
Content-Type: application/x-www-form-urlencoded
toAccount=attacker&amount=1000
攻击者可以在自己的恶意网站中嵌入:
<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="toAccount" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>
document.forms[0].submit();
</script>
当已登录银行网站的用户访问该恶意页面时,转账请求会自动发送并携带用户的会话 Cookie。
社交网站修改设置
某社交网站的邮箱修改接口:
// 前端代码
fetch('/api/update-email', {
method: 'POST',
body: JSON.stringify({ newEmail: 'attacker@example.com' }),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.token}`
}
})
攻击者可以构造钓鱼页面:
<a href="https://social.com/api/update-email?newEmail=attacker@example.com">
点击领取优惠券
</a>
CSRF 与 XSS 的区别
虽然都涉及跨站脚本,但两者有本质区别:
特性 | CSRF | XSS |
---|---|---|
攻击目标 | 利用用户身份执行操作 | 窃取用户数据或会话 |
依赖条件 | 需要用户已登录 | 需要存在注入漏洞 |
请求发起方 | 用户浏览器自动发起 | 恶意脚本主动执行 |
数据流向 | 从目标站点向外发出请求 | 外部脚本获取站点数据 |
防御措施的实现方案
同源检测
// 服务端中间件示例(Node.js)
app.use((req, res, next) => {
const origin = req.headers.origin || req.headers.referer;
if (!origin || !origin.match(/^https?:\/\/trusted\.com$/)) {
return res.status(403).send('Forbidden');
}
next();
});
CSRF Token 方案
<!-- 服务端渲染时注入Token -->
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<!-- 其他表单字段 -->
</form>
服务端验证:
// Express.js 使用 csrf 中间件
const csrf = require('csurf');
app.use(csrf({ cookie: true }));
app.post('/transfer', (req, res) => {
// 中间件会自动验证_csrf字段
});
SameSite Cookie 属性
// 设置Cookie时添加SameSite属性
res.cookie('sessionid', 'abc123', {
httpOnly: true,
secure: true,
sameSite: 'Strict'
});
双重提交Cookie
前端代码:
// 从Cookie中读取CSRF Token
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
// 发送请求时附加Header
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': getCookie('csrf_token')
}
});
现代前端框架的防护机制
React 的 CSRF 防护
// 在全局请求拦截器中添加Token
axios.interceptors.request.use(config => {
config.headers['X-XSRF-TOKEN'] = Cookies.get('XSRF-TOKEN');
return config;
});
Angular 的HttpClient
// 启用CSRF防护
import { HttpClientXsrfModule } from '@angular/common/http';
@NgModule({
imports: [
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN'
})
]
})
export class AppModule {}
实际开发中的注意事项
- 敏感操作二次验证:
// 关键操作要求重新输入密码
app.post('/delete-account', (req, res) => {
if (!verifyPassword(req.body.password)) {
return res.status(401).send('Invalid password');
}
// 执行删除操作
});
- API设计原则:
- 对状态修改操作强制使用POST/PUT/DELETE方法
- 避免使用GET请求执行写操作
- 为RESTful API添加版本控制前缀
- 日志监控:
// 记录敏感操作日志
app.post('/transfer', (req, res) => {
logAction({
userId: req.user.id,
action: 'TRANSFER',
metadata: {
toAccount: req.body.toAccount,
amount: req.body.amount,
ip: req.ip
}
});
});
高级攻击场景应对
针对AJAX请求的防护
// 自定义Header防护
const csrfToken = generateToken();
localStorage.setItem('csrf-token', csrfToken);
fetch('/api/data', {
headers: {
'X-Custom-CSRF': csrfToken
}
});
文件上传的CSRF防护
<!-- 文件上传表单需要特殊处理 -->
<form id="upload-form">
<input type="file" name="file">
<input type="hidden" name="_csrf" value="token123">
<button onclick="uploadWithProgress()">上传</button>
</form>
<script>
function uploadWithProgress() {
const formData = new FormData(document.getElementById('upload-form'));
// 确保Content-Type不被覆盖
fetch('/upload', {
method: 'POST',
body: formData,
headers: {
'X-CSRF-Verify': getCSRFToken()
}
});
}
</script>
移动端特有的CSRF问题
深度链接攻击
攻击者可以构造如下URL:
myapp://transfer?to=attacker&amount=1000
防御方案:
// Android Intent Filter验证
<intent-filter>
<data android:scheme="https" android:host="verified.domain"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
WebView中的防护
// 配置WebView的安全策略
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
// 验证URL域名白名单
if (!isTrustedDomain(request?.url?.host)) {
return true
}
return false
}
}
浏览器安全策略演进
Chrome 的SameSite默认变更
自Chrome 80起,Cookie的SameSite属性默认为Lax
:
Strict
: 完全禁止跨站发送Lax
: 允许顶级导航的GET请求None
: 允许跨站发送(需配合Secure)
Fetch Metadata规范
利用Sec-Fetch-*
请求头进行防护:
# Nginx配置示例
location /api/ {
if ($http_sec_fetch_site != "same-origin") {
return 403;
}
# 其他处理逻辑
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:自动化检测与工具推荐
下一篇:CSRF 攻击的典型场景