CORS

部分內容由 LLM 生成,尚未經過人工驗證。

Same-Origin Policy

瀏覽器預設安全機制:不同來源(Origin)的頁面無法存取彼此資源。

Origin = scheme + host + port

比較來源http://app.com 比較結果
http://app.com/api路徑不同,Origin 相同✅ 允許
https://app.comscheme 不同❌ 跨源
http://app.com:8080port 不同❌ 跨源
http://api.comhost 不同❌ 跨源

CORS(Cross-Origin Resource Sharing)讓伺服器透過 HTTP Header 宣告允許的來源,瀏覽器據此決定是否放行跨源請求。

CORS 機制

Simple Request

條件:GET / POST / HEAD,且僅使用簡單 Header(Content-Type: text/plain 等)。瀏覽器直接送請求,伺服器回應中附帶 CORS Header。

  sequenceDiagram
    participant B as Browser
    participant A as API (api.com)

    B->>A: GET /data
    Note over B,A: Origin: app.com
    A->>B: 200 OK
    Note over A,B: Access-Control-Allow-Origin: app.com
    Note over B: Origin 匹配 → 允許讀取回應

Preflight Request

條件:PUT / DELETE、自訂 Header、Content-Type: application/json 等非簡單請求。瀏覽器自動先送 OPTIONS 確認伺服器是否允許,通過後才送實際請求。

  sequenceDiagram
    participant B as Browser
    participant A as API (api.com)

    B->>A: OPTIONS /data
    Note over B,A: Origin: app.com<br/>Access-Control-Request-Method: DELETE

    A->>B: 204 No Content
    Note over A,B: Access-Control-Allow-Origin: app.com<br/>Access-Control-Allow-Methods: DELETE

    Note over B: Preflight 通過 → 送實際請求

    B->>A: DELETE /data
    Note over B,A: Origin: app.com

    A->>B: 200 OK

關鍵 Headers

方向Header說明
RequestOrigin請求來源
RequestAccess-Control-Request-MethodPreflight 宣告實際方法
RequestAccess-Control-Request-HeadersPreflight 宣告自訂 Header
ResponseAccess-Control-Allow-Origin允許的 Origin(* 或具體值)
ResponseAccess-Control-Allow-Methods允許的 HTTP 方法
ResponseAccess-Control-Allow-Headers允許的請求 Header
ResponseAccess-Control-Allow-Credentials是否允許攜帶 Cookie
ResponseAccess-Control-Max-AgePreflight 結果快取秒數
Access-Control-Allow-Origin: * 無法與 Access-Control-Allow-Credentials: true 同時使用。需攜帶 Cookie 時,必須指定具體的 Origin。

CSRF 攻擊與防禦

CSRF 攻擊流程

CSRF(Cross-Site Request Forgery):惡意網站誘使已登入使用者的瀏覽器,向目標網站發送非預期請求。

  sequenceDiagram
    participant U as User
    participant E as Evil (evil.com)
    participant BK as Bank (bank.com)

    U->>BK: POST /login
    BK->>U: Set-Cookie: session=abc

    U->>E: 瀏覽 evil.com
    E->>U: HTML 含隱藏表單 (action=bank.com/transfer)

    U->>BK: POST /transfer(Cookie 自動附加)
    BK->>U: 200 OK — 攻擊成功

關鍵:HTML form submit 是 Simple Request,不觸發 CORS Preflight,Cookie 仍由瀏覽器自動附帶。

CORS 如何阻擋 CSRF

對於使用 fetch / XMLHttpRequest 發起的跨源 API 請求,CORS 可有效阻擋:

  sequenceDiagram
    participant E as Evil (evil.com)
    participant B as Browser
    participant BK as Bank API (bank.com)

    E->>B: fetch('bank.com/transfer', {method: 'POST'})

    B->>BK: OPTIONS /transfer
    Note over B,BK: Origin: evil.com

    BK->>B: Access-Control-Allow-Origin: bank.com

    Note over B: evil.com ≠ bank.com → 阻擋請求

    B->>E: ❌ CORS policy blocked
CORS 僅保護 JSON API 請求(fetch / XHR);傳統 HTML form POST 仍可繞過,需搭配其他防禦手段。

其他防禦手段

方式防禦範圍
CSRF TokenForm + API
SameSite CookieForm + API
Double Submit CookieAPI(stateless 架構)
Origin / Referer 驗證輔助手段

CSRF Token

伺服器產生隨機 token 存入 session,回傳給前端。攻擊者在 evil.com 無法讀取 bank.com 的 HTML/response(SOP 擋住),故無法取得 token。

token 放置方式依請求類型而異:

請求類型token 位置
HTML form POST<input type="hidden" name="csrf_token" value="xyz"> → 送進 form body
JSON API(fetch)X-CSRF-Token: xyz custom request header
  sequenceDiagram
    participant U as User (app.com)
    participant S as Server

    U->>S: GET /form
    S->>U: HTML (input type=hidden csrf_token=xyz)
    Note over S: Session 存 xyz

    U->>S: POST /transfer
    Note over U,S: form body: amount=100 & csrf_token=xyz
    S->>S: 比對 Session token == form body token ✓
    S->>U: 200 OK

SameSite Cookie

後端 Set-Cookie 時設定 SameSite 屬性,瀏覽器自動決定跨站請求是否附帶 cookie,無需前端介入。

跨站 GET 導航跨站 POST說明
Strict完全不帶,最嚴格
Lax現代瀏覽器預設值
None必須加 Secure
  sequenceDiagram
    participant U as User
    participant E as Evil (evil.com)
    participant BK as Bank (bank.com)

    U->>BK: POST /login
    BK->>U: 200 OK
    Note over BK,U: Set-Cookie: session=abc SameSite=Lax

    U->>E: 瀏覽 evil.com
    E->>U: HTML 含隱藏表單

    U->>BK: POST /transfer
    Note over U,BK: Cookie 未附加 (SameSite=Lax 阻擋跨站 POST)
    BK->>U: 401 Unauthorized

Double Submit Cookie

適用於 stateless / 無 server-side session 架構(純 JWT API)。利用 SOP 讓攻擊者無法讀取 cookie 值,從而無法複製到 header。

  sequenceDiagram
    participant B as Browser (app.com)
    participant S as Server

    B->>S: GET /page
    S->>B: 200 OK
    Note over S,B: Set-Cookie: csrf=xyz (非 HttpOnly)

    Note over B: JS 讀取 csrf cookie 值
    B->>S: POST /transfer
    Note over B,S: Header: X-CSRF-Token: xyz<br/>Cookie: csrf=xyz

    S->>S: 比對 Header == Cookie ✓
    S->>B: 200 OK

evil.com 的 JS 無法讀 bank.com 的 cookie(SOP),所以無法在 header 填入正確值。

SameSite=Lax 普及後,Double Submit Cookie 多作為 legacy 相容手段,新專案優先考慮 CSRF Token 或 SameSite。

相關主題