文章中英模式
布鲁斯前端面试题目 - CSRF cookie跨域攻击
深入解析CSRF攻击原理、危害与防御机制。了解如何使用SameSite Cookie、CSRF Token等方法保护Web应用免受跨站请求伪造攻击。
文章中英模式
懒得看文章?那就来看视频吧
CSRF攻击是什么?
跨站请求伪造(CSRF)是一种攻击,让你在不知情的情况下执行非本意的操作。想象有人伪造你的签名去银行提款。
🔍 CSRF攻击原理
浏览器获得认证Cookie
包含自动发送请求(带上Cookie)的代码
银行网站误认为是你操作
简单CSRF攻击示例:
<!-- GET方式的CSRF攻击 -->
<img src="https://bank.com/transfer?to=hacker&amount=1000" style="display:none">
<!-- 当你访问含有此代码的页面时,自动发送转账请求 -->
<!-- POST方式的CSRF攻击 -->
<form id="csrf-form" action="https://bank.com/api/transfer" method="POST" style="display:none">
<input type="hidden" name="to" value="hacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>
// 页面加载后自动提交表单
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('csrf-form').submit();
});
</script>
CSRF攻击的工作原理
CSRF攻击的成功依赖于三个关键条件:
- 1. 用户已经在目标网站通过身份验证,并且存在包含认证信息的Cookie
- 2. Cookie自动随请求发送,即使请求来自不同网站
- 3. 攻击者诱使受害者访问恶意页面或点击恶意链接
攻击流程示例
情境设置:
- 1. 用户已登录银行网站(bank.com),浏览器储存了认证Cookie
- 2. 银行网站有一个转账API:
POST /api/transfer
- 3. 攻击者拥有一个恶意网站(evil.com)
步骤1: 用户已登录银行网站
// 用戶登入銀行網站後收到的Cookie
Set-Cookie: session=abc123; Domain=bank.com; Path=/; HttpOnly; Secure
步骤2: 攻击者准备恶意网站
<!-- 惡意網站(evil.com)上的HTML代碼 -->
<html>
<body>
<h1>贏取免費獎品!</h1>
<!-- 自動提交的隱藏表單 -->
<form id="csrf-form" action="https://bank.com/api/transfer" method="POST" style="display:none;">
<input type="hidden" name="to" value="attacker-account" />
<input type="hidden" name="amount" value="1000" />
</form>
<script>
// 頁面加載後自動提交表單
window.onload = function() {
document.getElementById("csrf-form").submit();
}
</script>
</body>
</html>
步骤3: 受害者访问恶意网站
当用户访问evil.com时,恶意脚本自动提交表单到bank.com,浏览器自动附加用户的银行Cookie。
POST /api/transfer HTTP/1.1
Host: bank.com
Cookie: session=abc123
Content-Type: application/x-www-form-urlencoded
to=attacker-account&amount=1000
步骤4: 银行网站处理请求
银行网站看到有效的session Cookie,认为请求来自合法用户,执行转账操作。
CSRF攻击的常见形式
1. GET请求攻击
利用图片标签或其他资源请求来执行攻击:
<!-- Malicious image tag triggers GET request -->
<img src="https://bank.com/api/transfer?to=attacker&amount=1000" width="0" height="0" />
2. POST请求攻击
如先前示例,使用自动提交的表单:
<form id="csrf-form" action="https://bank.com/api/transfer" method="POST">
<input type="hidden" name="to" value="attacker-account" />
<input type="hidden" name="amount" value="1000" />
</form>
<script>document.getElementById("csrf-form").submit();</script>
3. AJAX请求攻击
使用JavaScript发起XMLHttpRequest或fetch请求:
// 注意:這種方式通常會受到同源政策和CORS的限制
fetch('https://bank.com/api/transfer', {
method: 'POST',
credentials: 'include', // 關鍵:包含Cookie
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'to=attacker-account&amount=1000'
})
.then(response => console.log('CSRF攻擊成功'))
.catch(error => console.error('CSRF攻擊失敗', error));
4. 点击劫持(UI Redressing)
结合透明iframe和社会工程学,诱使用户无意中点击恶意按钮:
<style>
.game { position: relative; width: 500px; height: 500px; }
.overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; }
iframe { position: absolute; top: 0; left: 0; width: 500px; height: 500px; opacity: 0.0001; }
</style>
<div class="game">
<button class="overlay">Click to claim prize</button>
<!-- Semi-transparent iframe positioned above the bank site's 'Confirm Transfer' button -->
<iframe src="https://bank.com/transfer?to=attacker&amount=1000"></iframe>
</div>
CSRF攻击防御策略
❌ 常见错误观念
「CORS 限制跨站请求,就不能 CSRF」
错! 浏览器允许跨站提交表单(form submit)或发送图片请求(如 <img src="...">),这些方式不受 CORS 限制。
保护网站免受CSRF攻击的方法有多种,以下是最有效的防御策略:
1. SameSite Cookie 属性
这是目前最推荐的防御方法,通过设置Cookie的SameSite属性限制跨站请求发送Cookie:
Set-Cookie: session=abc123; SameSite=Strict; Path=/; HttpOnly; Secure
SameSite取值选项:
- 1. Strict:仅在相同站点发出的请求才会发送Cookie(最安全,但用户体验较差)
- 2. Lax:导航到目标网站的GET请求会发送Cookie,但其他跨站请求不会(平衡安全和用户体验,现代浏览器默认行为)
- 3. None:允许跨站发送Cookie,但必须同时设置Secure属性(不安全,仅在必要时使用)
2. CSRF Token
使用随机生成的令牌验证请求合法性:
服务器端生成并存储CSRF Token:
// Node.js Express示例
const crypto = require('crypto');
app.get('/form', (req, res) => {
// 生成隨機Token
const csrfToken = crypto.randomBytes(16).toString('hex');
// 存儲在用戶會話中
req.session.csrfToken = csrfToken;
// 在表單中嵌入Token
res.send(
'<form action="/api/transfer" method="POST">' +
'<input type="hidden" name="_csrf" value="' + csrfToken + '" />' +
'<!-- 其他表單字段 -->' +
'<button type="submit">確認轉帳</button>' +
'</form>'
);
});
服务器端验证Token:
// 中間件驗證CSRF Token
app.post('/api/transfer', (req, res) => {
// 從請求中獲取Token
const csrfToken = req.body._csrf;
// 驗證Token是否匹配
if (csrfToken !== req.session.csrfToken) {
return res.status(403).json({ error: 'CSRF token驗證失敗' });
}
// Token驗證成功,處理請求
// ...
});
CSRF Token有效是因为恶意网站无法获取跨域的令牌值,因此无法构造包含正确令牌的请求。
3. Double Submit Cookie
不需要服务器存储,同时将令牌存在Cookie和请求参数中:
// 設置一個僅用於CSRF保護的Cookie
app.get('/form', (req, res) => {
const csrfToken = crypto.randomBytes(16).toString('hex');
// 設置Cookie (需要加上SameSite=Lax增強安全性)
res.cookie('csrf', csrfToken, {
httpOnly: false,
sameSite: 'Lax',
secure: true
});
// 在表單中也包含相同的令牌
res.send(
'<form action="/api/transfer" method="POST">' +
'<input type="hidden" name="_csrf" value="' + csrfToken + '" />' +
'<!-- 其他表單字段 -->' +
'</form>'
);
});
// 驗證時比較Cookie中的令牌和表單提交的令牌是否一致
app.post('/api/transfer', (req, res) => {
if (req.cookies.csrf !== req.body._csrf) {
return res.status(403).json({ error: '驗證失敗' });
}
// 處理請求...
});
CSRF常见面试题
1. CSRF与XSS攻击的区别是什么?
答:两者攻击方式和目的不同:
CSRF
利用已登录状态发送伪造请求
无法读取数据,只能执行操作
防御:验证请求来源
XSS
注入并执行恶意脚本
可读取数据、执行任意JS
防御:过滤输入、编码输出
2. 为什么SameSite Cookie是防御CSRF的有效方法?
答:SameSite限制Cookie只在特定情况下发送:
Strict
仅同站点请求发送Cookie
Set-Cookie: session=123; SameSite=Strict;
Lax (现代浏览器默认)
允许顶级导航和GET请求带Cookie
Set-Cookie: session=123; SameSite=Lax;
CSRF攻击需要自动发送Cookie,SameSite直接阻止了这一机制。
3. CSRF Token的工作原理是什么?
答:通过不可预测的令牌验证请求来源:
服务器生成随机Token
放入表单或JS请求中
检查Token是否匹配
// 前端表單
<form>
<input type="hidden" name="_csrf" value="隨機令牌">
<!-- 其他表單字段 -->
</form>
// 後端驗證
if (req.body._csrf !== session.csrfToken) {
return res.status(403).send("拒絕請求");
}
由于同源政策,恶意网站无法读取目标网站的Token,无法构造有效请求。
4. 设置SameSite Cookie还需要CSRF Token吗?
答:通常不需要同时使用两种防御机制,但在某些情况下可能需要双重保护:
SameSite=Lax(推荐设置)通常已足够:
- 1. 阻止大多数CSRF攻击
- 2. 允许从外部网站的链接正常导航
- 3. 现代浏览器的默认行为
何时需要同时使用CSRF Token:
- 1. 需要支持较旧的浏览器(不支持SameSite属性)
- 2. 必须使用SameSite=None(例如第三方集成场景)
- 3. 需要更高安全等级的应用(如金融服务)
- 4. 防御深度策略(Defense in Depth)的实践
// 最佳實踐:結合使用
// 1. 設置SameSite Cookie
app.use(session({
cookie: {
secure: true,
httpOnly: true,
sameSite: 'lax' // 默認值,阻擋大多數CSRF
}
}));
// 2. 關鍵操作仍使用CSRF Token作為額外保護
app.post('/api/payment', csrfProtection, (req, res) => {
// 處理支付請求
});
安全性与兼容性的平衡:SameSite Cookie提供基础保护,而CSRF Token可在需要时提供额外安全层。