Web 安全系列之一(OWASP Top 10);最常與 CSRF 混淆,本篇徹底釐清。API 跨域場景見 API 設計範式。
目錄
- 先搞懂同源政策(SOP)
- 什麼是「同源」?
- 什麼是 CORS?
- CORS 怎麼運作
- 預檢請求(Preflight)
- 相關標頭
- 憑證與萬用字元的陷阱
- 關鍵釐清:CORS 是放寬,不是防護
- 常見誤區
- 常見問題
- 總結
先搞懂同源政策(SOP)
要懂 CORS,得先懂它放寬的對象——同源政策(Same-Origin Policy, SOP)。
SOP 是瀏覽器的預設安全機制:一個來源的網頁,其 JavaScript 預設不能讀取另一個來源的回應。這防止惡意網站偷讀你在其他網站的資料。
你在 evil.com 的頁面,JS 想 fetch bank.com 的資料並讀回應
→ 同源政策預設擋下「讀取回應」
CORS 就是「在受控前提下,放寬這個限制」的機制。
什麼是「同源」?
「同源」= 協定(scheme)+ 主機(host)+ 埠(port)三者全相同。
對照 https://app.example.com |
是否同源 | 原因 |
|---|---|---|
https://app.example.com/page |
✅ | 三者相同(路徑不影響) |
http://app.example.com |
❌ | 協定不同 |
https://api.example.com |
❌ | 主機不同(子網域也算) |
https://app.example.com:8080 |
❌ | 埠不同 |
任一不同就是「跨來源(cross-origin)」,預設受 SOP 限制。
什麼是 CORS?
CORS(Cross-Origin Resource Sharing,跨來源資源共享) 是一套**伺服器透過 HTTP 標頭,告訴瀏覽器「允許哪些來源讀取我的回應」**的機制。
瀏覽器:我(app.example.com)想讀 api.other.com 的回應
api.other.com 回應帶: Access-Control-Allow-Origin: https://app.example.com
→ 瀏覽器看到「我在允許名單內」→ 放行 JS 讀取回應
→ 否則 → 擋下,console 報 CORS 錯誤
關鍵:是伺服器用標頭「授權」,瀏覽器「執行」這個授權。
CORS 怎麼運作
簡單請求(Simple Request)
符合特定條件的請求(如 GET/POST、簡單標頭、特定 Content-Type)直接送出,瀏覽器再依回應的 Access-Control-Allow-Origin 決定是否讓 JS 讀回應:
GET /data HTTP/1.1
Origin: https://app.example.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com ← 伺服器授權
- 標頭符合 → JS 能讀回應
- 不符合 → 請求其實已送達伺服器、伺服器也處理了,但瀏覽器擋住 JS 讀取回應,並在 console 報錯
重點:CORS 擋的是「JS 讀回應」,不是「請求不發出」。這點對理解「CORS 不防 CSRF」很關鍵。
預檢請求(Preflight)
對「可能有副作用」的請求(如 PUT/DELETE、自訂標頭、特殊 Content-Type),瀏覽器會先送一個 OPTIONS 預檢問伺服器准不准,准了才送真正的請求:
OPTIONS /data HTTP/1.1 ← 預檢
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: PUT, POST, DELETE
Access-Control-Allow-Headers: Authorization
Access-Control-Max-Age: 86400 ← 預檢結果可快取多久
- 預檢通過 → 才送真正的
PUT Access-Control-Max-Age讓瀏覽器快取預檢結果,避免每次都先 OPTIONS
相關標頭
| 標頭(回應) | 作用 |
|---|---|
Access-Control-Allow-Origin |
允許哪個來源讀回應(具體來源或 *) |
Access-Control-Allow-Methods |
允許的 HTTP 方法(預檢用) |
Access-Control-Allow-Headers |
允許的自訂請求標頭(預檢用) |
Access-Control-Allow-Credentials |
是否允許帶憑證(cookie) |
Access-Control-Max-Age |
預檢結果可快取秒數 |
Access-Control-Expose-Headers |
允許 JS 讀取哪些回應標頭 |
| 標頭(請求) | 作用 |
|---|---|
Origin |
瀏覽器自動帶上的發起來源(不可由 JS 偽造) |
憑證與萬用字元的陷阱
要在跨來源請求帶 cookie/憑證(credentials: 'include')時,有嚴格限制:
Access-Control-Allow-Origin: * ← ❌ 帶憑證時「不允許」用萬用字元
Access-Control-Allow-Credentials: true
- 帶憑證時,
Access-Control-Allow-Origin必須是具體來源,不能是*,且要加Access-Control-Allow-Credentials: true - 這是安全設計:避免「任意網站 + 自動帶 cookie」讀走你的資料
設定 CORS 時別圖方便用
*+ credentials——瀏覽器會直接拒絕;正確做法是動態回填已驗證白名單內的具體Origin。
關鍵釐清:CORS 是放寬,不是防護
這是最多人誤解的地方,務必記住:
| 誤解 | 事實 |
|---|---|
| 「CORS 是保護我 API 的安全機制」 | CORS 是放寬同源限制的機制,本身不「保護」伺服器 |
| 「設了 CORS 就能擋壞人」 | CORS 只在瀏覽器生效;curl、server-to-server、Postman 完全不受 CORS 限制 |
| 「CORS 能防 CSRF」 | 不能。CSRF 不需要讀回應;CORS 管的正是「能不能讀回應」(見 CSRF) |
| 「CORS 錯誤代表請求被擋下」 | 請求多半已送達伺服器並執行,只是瀏覽器擋住 JS 讀取回應 |
CORS 管:瀏覽器中的 JS「能不能讀跨來源回應」
CORS 不管:請求發不發得出去、伺服器收不收、非瀏覽器客戶端
真正的存取控制:靠伺服器端的認證/授權(見 OWASP A01、認證與授權)
一句話:CORS 是「我允許誰來讀我」,不是「我擋住壞人」。 伺服器安全要靠認證授權,不是 CORS。
常見誤區
1. 「被 CORS 擋了 = 我的 API 安全」
完全相反。CORS 錯誤只代表瀏覽器不讓那個前端讀回應;攻擊者用非瀏覽器工具直接打你 API 不受任何 CORS 影響。安全要靠認證/授權。
2. 「前端報 CORS 錯,改前端就好」
CORS 由伺服器回應標頭決定,前端改不了。要在伺服器設正確的 Access-Control-Allow-*。
3. 「一律設 Access-Control-Allow-Origin: * 最省事」
公開唯讀 API 也許可以,但需要帶 cookie 的 API 不能用 *,且全開放可能讓任意網站讀取本應受限的資料。應回填白名單內的具體來源。
4. 「CORS 能防 CSRF」
不能。兩者是不同問題:CORS 管「讀回應」,CSRF 是「偽造會產生副作用的請求」。防 CSRF 用 SameSite/token(見 CSRF)。
常見問題
問題 1:CORS 到底是放寬還是限制?
放寬。預設的同源政策才是限制;CORS 是伺服器用標頭選擇性放寬「允許哪些來源讀我的回應」。
問題 2:為什麼會出現「blocked by CORS policy」?
你的前端(某來源)想讀另一來源的回應,但對方回應沒有(或不符)Access-Control-Allow-Origin,瀏覽器因而擋住 JS 讀取。請求通常已送達伺服器。
問題 3:CORS 錯誤要改前端還後端?
改後端——CORS 由伺服器回應標頭控制。前端無法靠自己「關掉」CORS(開發時頂多用代理繞過)。
問題 4:CORS 能保護我的 API 嗎?
不能當成保護。CORS 只在瀏覽器生效,非瀏覽器客戶端(curl/server)不受限。真正的保護靠伺服器端認證與授權(OWASP A01)。
問題 5:為什麼有時會先送一個 OPTIONS?
那是預檢請求。對可能有副作用的跨來源請求(PUT/DELETE、自訂標頭等),瀏覽器先用 OPTIONS 問伺服器准不准,通過後才送真正請求。
總結
核心要點
- 同源政策(SOP) 是預設限制:JS 不能讀跨來源回應;同源 = 協定+主機+埠全同
- CORS 是「放寬」SOP 的機制:伺服器用標頭授權哪些來源可讀回應,瀏覽器執行
- 流程:簡單請求直接送;有副作用的先送 OPTIONS 預檢
- 帶 cookie 時
Allow-Origin不能用*,要具體來源 +Allow-Credentials - CORS 是放寬不是防護:只在瀏覽器生效、不防 CSRF、非瀏覽器不受限;安全靠認證授權
- CORS 錯誤改後端標頭,且請求多半已送達
快速參考
| 概念 | 重點 |
|---|---|
| 同源 | 協定 + 主機 + 埠 全相同 |
| SOP | 預設:不能讀跨來源回應 |
| CORS | 伺服器授權放寬讀取 |
| 預檢 | 有副作用請求先送 OPTIONS |
| 帶憑證 | Allow-Origin 不可用 * |
| 本質 | 放寬「能不能讀」,非安全防護 |
建立日期:2026-06-18