魯斯前端布魯斯前端

文章中英模式

布魯斯前端面試題目 - CSRF cookie跨域攻擊

深入解析CSRF攻擊原理、危害與防禦機制。了解如何使用SameSite Cookie、CSRF Token等方法保護Web應用免受跨站請求偽造攻擊。

影片縮圖

懶得看文章?那就來看影片吧

CSRF攻擊是什麼?

跨站請求偽造(CSRF)是一種攻擊,讓你在不知情的情況下執行非本意的操作。想像成有人偽造你的簽名去銀行提款。

🔍 CSRF攻擊原理

1. 你登录银行网站(bank.com)

浏览器获得认证Cookie

2. 访问恶意网站(evil.com)

包含自动发送请求(带上Cookie)的代码

3. 恶意请求带上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. 1. 用戶已經在目標網站通過身份驗證,並且存在包含認證信息的Cookie
  2. 2. Cookie自動隨請求發送,即使請求來自不同網站
  3. 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的工作原理是什麼?

答:通過不可預測的令牌驗證請求來源:

1. 生成令牌

服務器生成隨機Token

2. 嵌入頁面

放入表單或JS請求中

3. 驗證令牌

檢查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可在需要時提供額外安全層。