Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

跨站請求偽造 (CSRF)

| , 3 minutes reading.

1. 定義

跨站請求偽造 (Cross-Site Request Forgery, CSRF) 是一種強制用戶在當前已登入的 Web 應用程式中執行非意願操作的攻擊。

它利用了瀏覽器的 「自動攜帶憑證(Ambient Authority)」 機制:基於目標網域,瀏覽器會自動在跨站請求中包含憑證(如 Session Cookie、Basic Auth 標頭),而不管該請求的觸發源頭。

2. 技術原理

核心漏洞不在於伺服器的身份驗證邏輯,而在於其無法驗證請求的真實意圖

當用戶登入 bank.com 時,瀏覽器存儲了會話 Cookie session_id=xyz。 如果用戶隨後訪問了惡意網站 evil.com,且該網站包含以下程式碼:

<form action="https://bank.com/transfer" method="POST" id="hack">
  <input type="hidden" name="amount" value="1000">
  <input type="hidden" name="to" value="attacker">
</form>
<script>document.getElementById('hack').submit();</script>

瀏覽器將執行此 POST 請求。關鍵點在於,瀏覽器會自動附加 session_id=xyz Cookie,因為目標地址是 bank.com。伺服器收到有效的 Session Cookie 後處理了轉帳,卻不知道請求實際上源自 evil.com

3. 攻擊流程 (序列圖)

sequenceDiagram
    participant Victim as 用戶瀏覽器
    participant Attacker as 惡意網站 (evil.com)
    participant Server as 目標伺服器 (bank.com)

    Note over Victim, Server: 用戶已登入 (Cookie: session=123)

    Victim->>Attacker: 1. 訪問惡意頁面
    Attacker-->>Victim: 2. 返回包含自動提交表單的頁面

    Note over Victim: 瀏覽器解析 HTML<br/>發現 <form action="bank.com">...

    Victim->>Server: 3. 發送 POST /transfer (包含 Cookie: session=123)
    Note right of Victim: 瀏覽器自動添加 Cookie<br/>Referer: https://evil.com

    Server-->>Server: 4. 驗證 Cookie (通過)<br/>5. 執行轉帳操作
    Server-->>Victim: 6. 返回 200 OK (轉帳成功)

4. 真實案例:Netflix (2006)

目標: Netflix 「添加到租賃隊列」功能。 漏洞類別: 基於 GET 的 CSRF。

2006 年,研究人員發現 Netflix 的「添加到隊列」操作是通過簡單的 GET 請求完成的: GET http://www.netflix.com/AddToQueue?movieid=70011204

攻擊方式: 攻擊者可以在網頁中植入一個圖片標籤: <img src="http://www.netflix.com/AddToQueue?movieid=70011204" />

當已登入 Netflix 的用戶載入攻擊者的頁面時:

  1. 瀏覽器嘗試從 Netflix 獲取「圖片」。
  2. 請求中包含了用戶活躍的 Netflix 認證 Cookie。
  3. Netflix 伺服器處理了該 GET 請求,並將特定電影添加到了受害者的租賃隊列中。
  4. 雖然這看起來影響較小,但它證明了可以在未經用戶許可的情況下觸發狀態變更。

影響: 導致了大規模的用戶數據操縱。這迫使 Netflix(以及整個行業)開始嚴格要求對狀態變更操作使用 POST 方法,並實施抗 CSRF 令牌。

5. 深度防禦策略

A. 同步令牌模式 (Synchronizer Token Pattern - 有狀態)

這是最穩固的防禦手段。

  1. 生成: 伺服器為用戶會話生成一個加密強度高、唯一且不可預測的隨機令牌(CSRF Token)。

  2. 傳輸: 將此令牌作為隱藏欄位嵌入到 HTML 表單中(或作為 AJAX 的特定 HTTP 標頭發送)。

    <input type="hidden" name="csrf_token" value="raNDomStr1ng...">
  3. 驗證: 在執行狀態變更請求(POST、PUT、DELETE)時,伺服器將提交的令牌與會話中存儲的令牌進行比對。

    • 如果令牌缺失或不匹配 -> 拒絕請求 (403 Forbidden)。
    • 注意: 令牌不得存儲在會被自動發送的 Cookie 中。

適用於 REST API 等無狀態後端。

  1. 伺服器在 Cookie 中設置一個隨機值(如 csrf_cookie),同時前端讀取該值並將其放入自定義標頭(如 X-CSRF-Token)中發送。
  2. 伺服器驗證 Cookie.csrf_cookie === Header.X-CSRF-Token
  3. 原理: 攻擊者可以觸發發送 Cookie 的請求,但受同源政策 (SOP) 限制,他們無法讀取 Cookie 值來設置自定義標頭。

瀏覽器層面的防禦(深度防禦)。 為會話 Cookie 設置 SameSite 屬性:

  • SameSite=Strict:跨站請求從不發送 Cookie。(安全性最高,但影響用戶體驗)。
  • SameSite=Lax:僅在安全的頂級導航(如 GET 連結)中發送 Cookie。(良好的平衡點)。
Set-Cookie: session_id=xyz; SameSite=Lax; Secure; HttpOnly

D. 標準標頭驗證 (Standard Headers Verification)

檢查 OriginReferer 標頭。

  • 如果存在 Origin,驗證其是否匹配目標網域。
  • 注意: 出於隱私原因,這些標頭可能會被瀏覽器(或代理)移除或剝離,因此這應作為一種「深度防禦」的輔助手段,而非主要防禦手段。

6. 參考資料