跨站請求偽造(CSRF)入門指南:攻擊原理、實例與防禦方法
更新日期: 2025 年 3 月 4 日
本文為 資安入門 系列文,第 7 篇
- OWASP Top 10:最新的 Web 安全風險與防範措施(2021 版)
- MD5 加密演算法是什麼?
- SHA-1 是什麼?為什麼不再安全?
- 深入解析 Session:從概念到運作方式
- 什麼是 Session Fixation(會話固定攻擊)?
- HTTP 與 HTTPS 的差別:新手完整指南
- 跨站請求偽造(CSRF)入門指南:攻擊原理、實例與防禦方法 👈所在位置
- 跨站指令碼攻擊(XSS)入門指南:從原理到防禦的全解析
- SQL 注入攻擊全解析:從入門到防禦實戰指南
- 新手指南:深入了解 Content-Security-Policy (CSP) 與網站安全
- 從零理解 Same-Origin Policy:瀏覽器安全的第一道防線
- 跨來源資源共享(CORS)完整指南:打破瀏覽器的安全邊界
在網路世界中,安全漏洞無處不在,而 跨站請求偽造(CSRF) 正是其中一種常見且危險的攻擊手法。
想像一下:當你登入銀行網站後,不小心點了一個不明連結,結果帳戶裡的錢竟被自動轉走——這就是 CSRF 攻擊的可怕之處。
本文將以 新手友善的視角,完整解析 CSRF 的運作原理、實際攻擊案例,以及開發者該如何防禦。
無論你是剛入門的工程師,還是對資安有興趣的學習者,都能透過這篇文章建立清晰的觀念。
什麼是跨站請求偽造(CSRF)?
CSRF(Cross-Site Request Forgery)是一種利用「用戶已登入的合法身分」發動攻擊的手法。
攻擊者誘導使用者在 不知情的情況下,透過瀏覽器向目標網站發送偽造的請求(例如轉帳、修改密碼),進而執行惡意操作。
以生活情境比喻,假設你有一張「銀行 VIP 會員卡」,只要出示卡片就能辦理業務。
攻擊者偷偷複製了你的卡片(盜用瀏覽器 Cookie),並偽造一張「轉帳申請單」讓你簽名(誘導點擊惡意連結)。
由於銀行只認卡片不認人,攻擊者便成功利用你的身分完成轉帳。
CSRF 攻擊如何運作?
具體流程
1. 用戶登入信任網站,瀏覽器儲存「身分通行證」
- 當用戶登入
example-bank.com
時,伺服器會發送 Session Cookie 給瀏覽器。 - Cookie 儲存機制:
- Cookie 由瀏覽器保存在用戶的電腦中(如 Chrome 的
Cookies
資料庫)。 - 每個 Cookie 嚴格綁定「域名」,例如
example-bank.com
的 Cookie 不會與evil-site.com
共用。
- Cookie 由瀏覽器保存在用戶的電腦中(如 Chrome 的
- 此時狀態:
用戶如同獲得「銀行 VIP 卡」,後續操作瀏覽器會自動出示此卡驗證身分。
2. 用戶誤入惡意網站,跨分頁的 Cookie 危機
- 用戶可能在另一個分頁或視窗開啟惡意連結(例如釣魚郵件中的
evil-site.com
)。 - 關鍵行為:
- 瀏覽器會 持續保留所有網站的 Cookie,除非手動清除或 Cookie 過期。
- 即使切換分頁,
example-bank.com
的 Cookie 仍存在瀏覽器中。
→ 這正是 CSRF 攻擊的核心漏洞!
3. 惡意網站發動「隱形攻擊指令」
- 攻擊者在
evil-site.com
頁面中暗藏以下程式碼:
<!-- 偽裝成圖片載入,實際發送轉帳請求 -->
<img src="https://example-bank.com/transfer?amount=10000&to=ATTACKER"
style="display:none">
或使用自動提交的表單:
<body onload="document.forms[0].submit()">
<form action="https://example-bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="to_account" value="ATTACKER_ACCOUNT">
</form>
</body>
4. 瀏覽器「自動帶 Cookie」的雙面刃
- 同源政策(Same-Origin Policy)的盲點:
- 瀏覽器禁止
evil-site.com
讀取example-bank.com
的 Cookie。 - 但允許
evil-site.com
發送請求 到example-bank.com
,並自動附加該域名的 Cookie!
→ 請求看似來自已登入用戶,伺服器難以辨別真偽。
- 瀏覽器禁止
- 攻擊請求的組成:
POST /transfer HTTP/1.1
Host: example-bank.com
Cookie: session_id=abc123 # 瀏覽器自動附加!
Content-Type: application/x-www-form-urlencoded
amount=10000&to_account=ATTACKER_ACCOUNT
5. 伺服器的致命誤判
- 伺服器檢查 Cookie 有效 → 認為是合法用戶的操作。
- 未經驗證直接執行轉帳指令,攻擊成功!
攻擊成功的三大必要條件
條件 | 技術細節 | 範例情境 |
---|---|---|
1. 用戶已登入且 Cookie 有效 | 瀏覽器儲存的 Session Cookie 未過期,且未設定 SameSite 防護屬性 | 用戶登入銀行後未登出,直接瀏覽其他網站 |
2. 目標網站缺乏 CSRF 防護 | 未使用 CSRF Token、未驗證 Referer、未設定 SameSite Cookie | 銀行允許純 Cookie 驗證請求,表單未嵌入隨機 Token |
3. 惡意請求可構造 | API 路徑與參數格式可預測(如固定 URL + 簡單參數) | 銀行使用 POST /transfer?amount=100&to=123 這樣無防護的 API 設計 |
為什麼 Cookie 機制會成為幫兇?技術深度解析
瀏覽器的「自動化便利」 vs 「安全風險」
- 設計初衷:
Cookie 是為了讓用戶免於重複登入(例如保持購物車狀態),卻被攻擊者反向利用。 - 跨站請求的矛盾:
- 瀏覽器的自動化便利性,導致跨站請求「帶著合法身分」闖關成功。
Same-Origin Policy 的不足
- 僅限制「讀取」跨站資料,卻允許「發送」跨站請求。
→ 攻擊者不需要竊取 Cookie,只需「借用」瀏覽器自動帶上的 Cookie。
實例演練:一個簡單的 CSRF 攻擊
情境設定
假設某銀行提供以下轉帳 API:
POST /transfer
參數:
- amount=1000 # 轉帳金額
- to_account=12345 # 目標帳戶
正常操作流程
用戶在銀行網站手動填寫表單,送出轉帳請求:
<!-- 合法表單 -->
<form action="https://example-bank.com/transfer" method="POST">
<input type="text" name="amount" value="1000">
<input type="text" name="to_account" value="12345">
<button>確認轉帳</button>
</form>
攻擊者構造的惡意頁面
攻擊者建立一個看似無害的頁面,內藏自動提交的表單:
<!-- 惡意頁面 -->
<body onload="document.forms[0].submit()">
<form action="https://example-bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="to_account" value="ATTACKER_ACCOUNT">
</form>
</body>
當已登入銀行的用戶訪問此頁面時,瀏覽器會自動發送轉帳請求,完成攻擊!
如何防禦 CSRF 攻擊?
CSRF Token 驗證
▶ 原理:伺服器生成隨機 Token 嵌入表單,提交時驗證是否匹配。
▶ 實作範例:
<!-- 表單中嵌入 Token -->
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="RANDOM_STRING">
<!-- 其他表單欄位 -->
</form>
# 伺服器端驗證
if request.form['csrf_token'] != session['csrf_token']:
return "Invalid CSRF Token", 403
CSRF Token 的設計核心
CSRF Token 是一種 由伺服器生成的隨機加密字串,其防禦效果依賴以下關鍵特性:
特性 | 技術原理 | 對抗 CSRF 攻擊的作用 |
---|---|---|
隨機性 | 每次表單生成時動態產生(如 UUID v4、加密亂數) | 攻擊者無法預測或構造有效 Token |
與 Session 綁定 | Token 儲存在伺服器的 Session 中,與當前用戶綁定 | 確保 Token 僅對特定用戶有效 |
單次使用 | 部分實作中,Token 提交後立即失效(可選策略) | 防止重放攻擊(Replay Attack) |
同源限制 | Token 只能由合法網站的前端程式碼讀取(遵守同源政策) | 攻擊者無法從跨站頁面竊取 Token |
攻擊者為什麼無法取得 Token?
情境模擬:攻擊者構造惡意表單
假設攻擊者嘗試在 evil-site.com
建立以下表單:
<!-- 惡意頁面 -->
<form action="https://example-bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="to_account" value="ATTACKER">
<!-- 攻擊者猜測或偽造 Token -->
<input type="hidden" name="csrf_token" value="FAKE_TOKEN">
</form>
關鍵限制:瀏覽器的同源政策(Same-Origin Policy)
- 攻擊者無法讀取合法網站的 Token
- 即使使用者已登入
example-bank.com
,evil-site.com
的前端 JavaScript 無法跨域讀取 合法網站的 DOM 內容(包含 Token)。 - 例:若 Token 嵌入在
<input type="hidden" name="csrf_token" value="abc123">
,攻擊者無法透過 JS 取得此值。
- 即使使用者已登入
- Token 不存在於 Cookie 中
- 伺服器將 Token 儲存在 Session 而非 Cookie,因此瀏覽器 不會自動攜帶 Token 在請求中。
CSRF Token 設計哲學
- 隨機性:Token 必須足夠亂數,無法被暴力破解或預測。
- 與用戶綁定:透過 Session 確保 Token 專屬於當前用戶。
- 同源隔離:依賴瀏覽器同源政策,防止跨站竊取 Token。
「CSRF Token 是伺服器給用戶的一個動態密碼,攻擊者既看不到也猜不到,因此無法偽造合法請求。」
SameSite Cookie 屬性
▶ 原理:限制 Cookie 僅在相同網站(Same Site)的請求中傳送。
▶ 設定方法(HTTP Response Header):
Set-Cookie: session_id=abc123; SameSite=Strict
Strict
:完全禁止跨站請求帶 Cookie(適用敏感操作)Lax
:允許部分安全跨站請求(例如導航到目標網站)
驗證請求來源(Referer Header)
▶ 原理:檢查 HTTP 請求的 Referer
標頭是否來自合法域名。
▶ 注意:某些瀏覽器可能不發送 Referer,且攻擊者可偽造此值(需搭配其他機制)。
Referer 是什麼?用現實比喻快速理解
什麼是 Referer Header?
- 技術定義:
HTTP 請求中的Referer
(註:拼寫錯誤是歷史遺留問題,正確應為Referrer
)標頭,代表「這個請求是從哪個網頁連結過來的」。
例如:當你點擊news-site.com
上的連結進入shop.com
,shop.com
的伺服器會看到:
GET /product/123 HTTP/1.1
Host: shop.com
Referer: https://news-site.com/article/456
- 比喻理解:
Referer
就像「快遞包裹上的寄件人地址」。
伺服器(收件人)可以透過地址(Referer)知道包裹(請求)是從哪裡寄來的。
為什麼檢查 Referer 能防禦 CSRF?
正常操作 vs CSRF 攻擊的 Referer 差異
- 合法操作情境:
用戶在example-bank.com
的轉帳頁面提交表單 → Referer 是https://example-bank.com/transfer
。 - CSRF 攻擊情境:
用戶在evil-site.com
被誘導發送轉帳請求 → Referer 是https://evil-site.com/fake-page
。
防禦原理
伺服器檢查 Referer 是否來自「自己家的域名」,如果不是就拒絕請求。
例如:
# 伺服器端檢查 Referer 的偽代碼
def process_request(request):
referer = request.headers.get('Referer')
if not referer.startswith('https://example-bank.com'):
return "Request blocked: Invalid Referer", 403
# 繼續處理請求...
為什麼不能單獨依賴 Referer 檢查?
瀏覽器可能不發送 Referer
- 隱私設定影響:
某些瀏覽器(如 Firefox 嚴格模式、Brave)或插件(如防追蹤工具)會屏蔽 Referer。 - 從 HTTPS 到 HTTP 的請求:
安全政策規定,若當前頁面是 HTTPS,而請求目標是 HTTP,瀏覽器 不會發送 Referer。
案例模擬:
用戶在 https://example-bank.com
登入後,從 http://evil-site.com
觸發 CSRF 攻擊:
→ 請求的 Referer 被屏蔽 → 伺服器誤判合法請求!
攻擊者可偽造 Referer(特定條件下)
- 透過中間攻擊手法:
如果網站存在 開放重定向(Open Redirect)漏洞,攻擊者可構造看似合法的 Referer。
例如:
Referer: https://example-bank.com/redirect?url=evil-site.com
- 利用瀏覽器特性:
某些舊版瀏覽器允許透過meta
標籤或 JavaScript 修改 Referer,但現代瀏覽器已修復此問題。
跨域子域名的混淆風險
- 錯誤配置案例:
若伺服器允許所有子域名(如*.example-bank.com
),攻擊者可能註冊evil.example-bank.com
發動攻擊。
→ Referer 檢查通過,但實際是跨站請求!
實務建議:如何安全使用 Referer 檢查?
僅作為「補充防禦」
- 絕對不要單獨依賴 Referer,需搭配以下任一機制:
- CSRF Token
- SameSite Cookie
- 自定義請求標頭(如
X-Requested-With
)
嚴格驗證邏輯
- 檢查 Referer 的 協議(HTTP/HTTPS)、完整域名、端口。
- 拒絕空 Referer 或非預期來源。
其他防禦手段
- 雙重提交 Cookie:將 Token 同時存在 Cookie 和表單,伺服器比對兩者。
- 自定義 Header:要求 AJAX 請求包含特定 Header(例如
X-Requested-With
)。 - 敏感操作二次驗證:例如要求重新輸入密碼或簡訊驗證碼。
總結與最佳實踐
CSRF 攻擊的關鍵在於 濫用瀏覽器的自動帶 Cookie 機制,而防禦的核心是「確保請求來自合法來源」。
開發者應至少實作以下措施:
- 為所有敏感操作啟用 CSRF Token。
- 為 Session Cookie 設定 SameSite 屬性。
- 定期更新框架與函式庫(如 Django、Spring Security 已內建 CSRF 防護)。
資安沒有銀彈,唯有持續學習與多層次防護,才能打造更安全的網路環境!