紀錄工作經驗、相關知識,解決技術相關問題。

工作雜談, 網站相關, 資訊相關

CSRF 跨站請求偽造攻擊原理與 Anti-CSRF Token 防禦實作

CSRF 跨站請求偽造攻擊原理與 Anti-CSRF Token 防禦實戰 特色圖片

在網路安全領域中,瀏覽器會自動帶入 Cookie 的特性雖然為使用者帶來了無縫的登入體驗,但也成了跨站請求偽造(CSRF)攻擊的主要溫床。本文將深入探討 CSRF 的攻擊原理與防禦策略,並提供實際的程式碼範例。

想了解基礎 HTML 標籤結構與語意化細節,可先前往觀看 〈HTML footer 標籤語意化誤區解析:全域頁尾、區段結尾與內容模型規範〉。

一、CSRF 跨站請求偽造的核心原理與攻擊場景

在網路安全領域中,跨站請求偽造(CSRF 或 XSRF)是一種常見的攻擊方式,它允許惡意網站代表無意中的使用者執行非授權操作。當使用者登入信任網站後,瀏覽器在每次發送請求時,會自動在 HTTP 標頭中夾帶該網站的 Cookie 憑證。惡意網站正是利用了這個瀏覽器自動帶入 Cookie 的機制,在使用者不知情的情況下發動攻擊,對網路安全構成了嚴重威脅。

CSRF 攻擊主要是利用了網站對用戶瀏覽器的盲目信任。當用戶登入一個網站 A 後,在不登出的情況下訪問另一個惡意網站 B。網站 B 可以在用戶不知情的情況下,代表用戶向網站 A 發送請求,如提交表單、更改設定等操作,而這些請求會自動包含用戶的認證資訊(如 Cookies),使得網站 A 誤認為是合法用戶的操作。

攻擊示例情境:
假設一個用戶已經登入了銀行網站 A,在此期間,該用戶點擊了一個來自惡意網站 B 的連結。該連結實際上是一個向銀行網站 A 發送轉帳請求的表單。由於用戶的瀏覽器已經包含了登入網站 A 的 Cookie,因此該請求會被網站 A 視為有效請求,從而完成未經授權的轉帳操作。

二、防禦 CSRF 攻擊的 3 大核心策略與規格對照

為了有效防禦 CSRF 攻擊,開發者可透過請求驗證機制、來源檢查與瀏覽器 Cookie 屬性進行多層防護。以下為常見防禦機制的特性對照:

防禦策略運作機制與核心原理優點與適用場景限制與注意事項
Anti-CSRF Token伺服器生成唯一的隨機密鑰並嵌入表單或 AJAX 請求中,提交時進行雜湊驗證。防禦力極高,不依賴瀏覽器特性,適用於所有狀態變更的 POST/PUT 請求。需要在前後端架構中額外處理 Token 的傳遞與儲存邏輯。
SameSite Cookie 屬性限制 Cookies 隨跨站請求發送,提供 Strict、Lax 與 None 三種層級設定。瀏覽器原生支援,設定簡易,能有效阻擋多數自動帶入 Cookie 的跨站請求。須注意 Lax 在 GET 請求與頂層導覽下的防禦盲點。
Referer Header 檢查伺服器端檢查請求標頭的來源網域是否符合白名單。實作成本低,可作為輔助驗證防線。部分瀏覽器或隱私模式會遮蔽 Referer,存在被繞過的風險。

1. 使用 Anti-CSRF Token

最有效的防禦手段之一是在表單或 AJAX 請求中使用 Anti-CSRF Token。這是一個隨機生成的唯一標識符,伺服器在渲染頁面時生成並嵌入到頁面中,客戶端提交請求時必須包含此 Token,伺服器再進行驗證。

以下為 HTML 表單內嵌 Hidden Token 的實作範例:

<form action="/transfer" method="POST">
    <!-- 伺服器動態生成的 Anti-CSRF Token -->
    <input type="hidden" name="csrf_token" value="d97a1d1d82d4282282adbf4f23b6b85d">
    
    <label for="amount">轉帳金額:</label>
    <input type="text" id="amount" name="amount">
    
    <label for="to_account">收款帳號:</label>
    <input type="text" id="to_account" name="to_account">
    
    <button type="submit">確認轉帳</button>
</form>

2. 使用 SameSite Cookie 屬性

設定 Cookies 的 SameSite 屬性可以限制 Cookies 隨跨站請求發送,從而幫助防止 CSRF 攻擊。SameSite 屬性可以設為 Strict、Lax 或 None。

以下為使用 Express.js 設定 Cookie 的 SameSite 屬性實作範例:

const express = require('express');
const app = express();

app.get('/login', (req, res) => {
    // 設定含有 SameSite 與 Secure 屬性的 Cookie
    res.cookie('session_id', 'xyz123securetoken', {
        httpOnly: true,
        secure: true,   // 僅透過 HTTPS 傳輸
        sameSite: 'lax' // 可設為 'strict' 或 'lax'
    });
    
    res.send('登入成功並已寫入 Cookie');
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

三、實務踩坑排查與防呆設計

在實務專案開發中,許多團隊在導入 Anti-CSRF Token 與 Cookie 設定時,常因分散式架構或屬性誤解而留下資安漏洞,以下整理兩大高頻踩坑情境與對應解法:

1. Token 狀態儲存與分散式架構(Session vs Stateless JWT)

  • 踩坑情境與觸發原因:許多開發者在實作 Anti-CSRF Token 時,誤以為只要在前端埋入隨機字串即可。但在現代分散式微服務或負載平衡(Load Balancer)架構下,若 Token 僅存在單一伺服器的記憶體(In-Memory)或傳統 Session 中,當使用者的請求被導向另一台伺服器時就會驗證失敗,導致合約表單提交不斷報錯。
  • 實務防呆與排錯解法:應將 Token 綁定於使用者的加密 Session(如 Redis 集中式快取),或者採用「雙重 Cookie 驗證(Double Submit Cookie pattern)」,將 Token 同時寫入前端不可被 JavaScript 讀取的 HttpOnly Cookie 與表單隱藏欄位中進行雜湊比對。

2. SameSite=Lax 在跨站 POST 請求的防禦盲點

  • 踩坑情境與觸發原因:開發者常以為設定了 SameSite=Lax 就能萬事大吉。然而,Lax 在用戶透過「頂層導覽(Top-Level Navigation)」且使用安全 HTTP 方法(如 GET)時允許攜帶 Cookie。若系統的某些重要狀態變更(例如刪除帳號、轉帳)設計成透過 GET 請求觸發,SameSite=Lax 將完全無法阻擋 CSRF 攻擊。
  • 實務防呆與排錯解法:嚴格落實 RESTful API 設計規範,所有會變更伺服器狀態(State-Changing)的操作(如 POST、PUT、DELETE)絕對不能使用 GET 方法;針對高敏感操作,應強制採用 SameSite=Strict 或搭配 Anti-CSRF Token 雙重防護。

四、常見問題 (FAQ)

Q1:CSRF 攻擊跟 XSS 攻擊有什麼差別?

  • 精煉解答:兩者最大的差別在於「攻擊目標與信任鏈」。XSS(跨網站指令碼)是攻擊者注入惡意 JavaScript 來「直接竊取使用者的 Cookie 或憑證」(信任你的網站);而 CSRF(跨站請求偽造)則是利用瀏覽器「會自動帶入 Cookie」的特性,欺騙受害者的瀏覽器去「冒充使用者發送未授權的合法請求」(信任使用者的瀏覽器)。簡單來說,XSS 是偷你的鑰匙,CSRF 是趁你沒注意時拿你的鑰匙去開門。

Q2:為什麼 SPA 單頁應用(如 React / Vue)搭配 JWT 還會受到 CSRF 攻擊?

  • 精煉解答:這取決於你的 JWT 存在哪裡。如果你的 JWT 或 Access Token 是存放在 localStorage 中,透過 JavaScript 每次在 Header(如 Authorization: Bearer)手動夾帶,因為惡意網站無法讀取跨網域的 localStorage,所以天然免疫傳統 CSRF。但如果你的 JWT 是存放在由瀏覽器自動管理的 Cookie 中,當使用者造訪惡意網站並觸發請求時,瀏覽器依然會自動帶上該 Cookie,這時就會受到 CSRF 攻擊。

Q3:SameSite=Strict 與 SameSite=Lax 到底該怎麼選擇?

  • 精煉解答:
    • Strict:最安全,任何跨站請求(包含從外部網站點擊連結導向你的網站)都不會攜帶 Cookie。缺點是使用者點擊外部連結進到你的網站時會變成「未登入」狀態,影響使用者體驗。
    • Lax:多數情況下的平衡選擇。它會阻擋大多數跨站的 POST、PUT、DELETE 請求,但在「頂層導覽(Top-Level Navigation)」且使用安全的 GET 方法(例如使用者點擊外部連結來到你的網站)時允許攜帶 Cookie。建議高敏感系統(如網路銀行)採用 Strict,一般網站則可使用 Lax 並搭配 Anti-CSRF Token。

Q4:如果網站使用 RESTful API 並採用 Bearer Token 驗證,還需要實作 Anti-CSRF 嗎?

  • 精煉解答:如果你的 API 架構完全採用 Token-based Authentication(且 Token 是存在 localStorage 或記憶體中,並由前端 JavaScript 在每次請求的 Authorization 標頭中手動帶入),由於瀏覽器不會自動將其附加到跨站請求中,因此不需要實作 Anti-CSRF Token。但若你的 API 是依賴 Session Cookie 或把 JWT 存在 Cookie 中自動傳遞,就必須實作 Anti-CSRF 防禦。

總結

理解並預防 CSRF 攻擊對於保障網路安全至關重要。透過實施有效的安全策略,如使用 Anti-CSRF Token 和配置 SameSite Cookie 屬性,開發者可以顯著減少這種攻擊的風險。隨著技術的發展,網路安全領域的威脅和防禦措施也在不斷進化,持續的學習和適應是保護網站和用戶安全的關鍵。

參考資料與延伸閱讀

發表迴響