屬於 OWASP Top 10 範疇;常與 XSS 及 CORS 混淆,本篇特別釐清。
目錄
- 什麼是 CSRF?
- 為什麼 CSRF 可行
- 攻擊範例
- CSRF vs XSS
- 防護一:SameSite Cookie
- 防護二:CSRF Token
- 防護三:檢查 Origin / Referer
- 防護四:避免用 GET 做有副作用的操作
- CSRF 與 CORS 的關係(常見混淆)
- 常見問題
- 總結
什麼是 CSRF?
CSRF(Cross-Site Request Forgery,跨站請求偽造) 是誘使已登入的受害者,在不知情下對目標網站送出他本意之外的請求。攻擊者「借用」受害者已通過認證的身分去執行操作。
受害者已登入 bank.com(瀏覽器有 bank.com 的 cookie)
→ 受害者被誘導開啟攻擊者的網頁
→ 該網頁偷偷對 bank.com 送出「轉帳」請求
→ 瀏覽器自動帶上 bank.com 的 cookie → 銀行以為是受害者本人操作
關鍵:攻擊者看不到回應,但能讓有副作用的操作發生(轉帳、改密碼、刪資料)。
為什麼 CSRF 可行
根因:瀏覽器在跨站請求時,會自動帶上目標網站的 cookie。
對 bank.com 的任何請求(不管從哪個網頁發起)
→ 瀏覽器自動附上 bank.com 的 session cookie
→ 伺服器只看 cookie 判斷身分 → 無法分辨是本人還是被偽造的請求
只要網站只靠 cookie 判斷身分、且不驗證請求來源,就可能被 CSRF。
攻擊範例
用表單自動送出(POST)
<!-- 攻擊者的網頁 -->
<form action="https://bank.com/transfer" method="POST" id="f">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById("f").submit();</script>
<!-- 受害者一開這頁,就自動對 bank.com 送出轉帳,並自動帶上其 cookie -->
用 GET(更簡單,若該操作竟用 GET)
<!-- 若轉帳能用 GET 觸發,一張圖就夠 -->
<img src="https://bank.com/transfer?to=attacker&amount=10000">
這也是「有副作用的操作絕不能用 GET」的原因之一。
CSRF vs XSS
兩者常被搞混,但機制完全不同:
| 面向 | CSRF | XSS |
|---|---|---|
| 本質 | 偽造請求(借用既有登入) | 注入腳本(在受害者瀏覽器執行) |
| 攻擊者能讀回應嗎 | 不能(被同源政策擋) | 能(腳本在受害者頁面內) |
| 依賴 | 瀏覽器自動帶 cookie | 頁面有可注入點 |
| 危害 | 以受害者身分執行有副作用操作 | 幾乎全能(偷 token、冒名、側錄) |
| 主防護 | SameSite / CSRF token / 驗來源 | 輸出編碼 / CSP / 淨化 |
一句話:CSRF 是「借你的手做事」,XSS 是「在你家裡植入間諜」。 XSS 通常更嚴重(且 XSS 可繞過多數 CSRF 防護)。
防護一:SameSite Cookie
現代最主要、最省力的防線:給 cookie 設 SameSite,限制它在跨站請求時是否送出。
Set-Cookie: session=abc; SameSite=Lax; Secure; HttpOnly
| 值 | 行為 |
|---|---|
Strict |
跨站請求完全不送 cookie(最嚴,但從外站連回來會像未登入) |
Lax(多數瀏覽器預設) |
跨站的安全導覽(GET 連結)會送,但跨站 POST/表單不送 → 擋掉多數 CSRF |
None |
一律送(需配 Secure)——跨站情境才用 |
Lax預設已能擋掉大部分 CSRF(跨站 POST 不帶 cookie)- 但
Lax對「跨站 GET」仍送 cookie → 所以有副作用操作別用 GET
防護二:CSRF Token
傳統且可靠的防線:Synchronizer Token Pattern。
伺服器產生隨機 CSRF token → 嵌進表單(隱藏欄位)/ 回給前端
提交時必須帶上這個 token → 伺服器驗證
<form method="POST" action="/transfer">
<input type="hidden" name="csrf_token" value="隨機且綁定 session 的值">
...
</form>
- 攻擊者的網頁拿不到這個 token(同源政策擋住它讀取受害者頁面內容)→ 偽造的請求缺 token → 被拒
- 變體 Double-Submit Cookie:token 同時放 cookie 與請求參數,伺服器比對兩者是否一致(不需 server 端儲存)
防護三:檢查 Origin / Referer
伺服器檢查請求的 Origin 或 Referer 標頭是否來自自己的網站:
若 Origin 不是 https://bank.com → 拒絕
- 瀏覽器會在跨站請求帶上
Origin,攻擊者無法偽造(JS 改不了這些受保護標頭) - 常作為 SameSite / token 之外的額外驗證
防護四:避免用 GET 做有副作用的操作
安全的 HTTP 方法語意本身就是一道防線:
GET應該是安全的(純讀取、無副作用)——見 REST 的安全性與冪等性- 把轉帳、刪除等放在
POST/PUT/DELETE,就避免「一張<img>或一個連結」觸發 - 配合
SameSite=Lax(跨站 POST 不帶 cookie),效果更好
CSRF 與 CORS 的關係(常見混淆)
這是最多人搞錯的地方:CORS 不能防 CSRF,甚至方向相反。
CORS:控制「JavaScript 能不能讀取跨來源回應」
CSRF:偽造請求讓副作用發生(攻擊者根本不需要讀回應)
- CSRF 攻擊不需要讀回應——它只要讓「轉帳發生」即可,所以 CORS 擋不到它
- CORS 是放寬跨來源「讀取」的機制,不是「防護」機制;設錯 CORS 反而擴大風險
- 防 CSRF 要靠 SameSite / CSRF token / 驗 Origin,不是靠 CORS
詳細的 CORS 機制見 CORS 篇(同分類)。記住:CORS 管「能不能讀」,CSRF 管「請求被不被偽造」,是兩回事。
常見問題
問題 1:CSRF 和 XSS 差在哪?
CSRF 偽造請求、借用受害者既有登入做有副作用操作,攻擊者讀不到回應;XSS 注入腳本在受害者瀏覽器執行,幾乎全能。XSS 通常更嚴重,且能繞過 CSRF 防護。
問題 2:有了 SameSite 還需要 CSRF token 嗎?
SameSite=Lax 已擋多數 CSRF,但仍建議縱深防禦:對重要操作加 CSRF token 或驗 Origin,以防瀏覽器差異、SameSite=None 的場景、或子網域問題。
問題 3:CORS 可以防 CSRF 嗎?
不能。CORS 管的是「JS 能否讀取跨來源回應」,而 CSRF 不需要讀回應、只要讓請求發生。防 CSRF 要用 SameSite / token / Origin 檢查。
問題 4:純 token(放 Authorization 標頭、不靠 cookie)還會 CSRF 嗎?
若身分驗證不靠瀏覽器自動帶的 cookie,而是 JS 主動把 token 放進 Authorization 標頭,攻擊者的網頁無法替你加這個標頭 → 天生較不受 CSRF 影響。但這類 token 若存 localStorage,又更暴露於 XSS——是一個取捨。
問題 5:為什麼有副作用的操作不能用 GET?
因為 GET 太容易被觸發(<img>、連結、預載),且 SameSite=Lax 對跨站 GET 仍會帶 cookie。把副作用放 POST/PUT/DELETE 能避免這些被動觸發。
總結
核心要點
- CSRF:誘使已登入受害者送出非本意請求,借用其登入身分執行有副作用操作
- 根因:瀏覽器跨站請求會自動帶 cookie,伺服器只看 cookie 就無法分辨真偽
- 攻擊者讀不到回應,所以 CORS 擋不了 CSRF(兩者是不同問題)
- 主防護:
SameSite=Lax/Strictcookie(現代主力)+ CSRF token + 驗 Origin/Referer - 有副作用操作別用 GET
- 與 XSS 區別:CSRF 偽造請求、XSS 注入腳本;XSS 通常更嚴重且能繞過 CSRF 防護
快速參考
| 防護 | 作用 |
|---|---|
SameSite=Lax/Strict |
跨站請求不帶 cookie(主力) |
| CSRF token | 攻擊者拿不到 → 偽造請求缺 token 被拒 |
| 驗 Origin/Referer | 確認請求來自本站 |
| 副作用用 POST | 避免被 <img>/連結被動觸發 |
| ⚠️ CORS | 不能防 CSRF(管的是「能否讀回應」) |
建立日期:2026-06-18