文章中英模式
布魯斯前端面試題目 - 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請求攻擊
利用圖片標籤或其他資源請求來執行攻擊:
<!-- 惡意圖片標籤載入時會觸發GET請求 -->
<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">點擊領取獎品</button>
<!-- 半透明iframe,定位到銀行網站的"確認轉帳"按鈕上方 -->
<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可在需要時提供額外安全層。