CORS for Web Developer

寫給網頁開發者的 CORS 理解

前言

最近在碰一些資安的東東,發現自己對於這一塊知識都比較薄弱,身為前端工程師資安通常並不是首要關注的職責,但一但有漏洞保證後果不堪設想,於是近期來補足一下這方面的知識。

什麼是 CORS

CORS (Cross-Origin Resource Sharing) 跨來源資源共享,是一個機制用來決定網頁是否能夠存取其他來源的資源。

在提到 CORS 之前會先需要了解瀏覽器的同源政策(Same-Origin Policy),同源政策是瀏覽器的一個安全機制,限制了不同來源的網頁之間的互動,網頁來源可以被定義為三個部分:

  • 協議(Protocol):httphttps
  • 主機(Host):www.example.com
  • 端口(Port):80443

只有當這三個部分都相同時,瀏覽器才會認為是同源,否則就是跨來源。CORS 就是瀏覽器提供的防護網用來決定是否允許跨來源的請求。而這一項設定通常會需要在伺服端調整 CORS 相關設定。

CORS 設定

CORS 設定會在伺服器端進行,透過設定 HTTP 標頭來告訴瀏覽器是否允許跨來源請求。具體來說瀏覽器會將請求分為兩種:

簡單請求

  1. 只能是 GETPOSTHEAD 方法
  2. 自訂的 header 只能是 AcceptAccept-LanguageContent-LanguageContent-Type(值只能是 application/x-www-form-urlencodedmultipart/form-datatext/plain)根據 Fetch Spec🔗
  3. 發出請求的物件沒有註冊事件監聽器
  4. 請求中沒有 ReadableStream 的物件被用於上傳

簡單請求下 header 會帶上 Origin 來告訴伺服器請求的來源

GET /doc HTTP/1.1
Origin: Server-b.com

伺服器會根據這個來源來決定是否允許跨來源請求,如果允許的話伺服器會在回應中加上 Access-Control-Allow-Origin 來告訴瀏覽器這個來源是被允許的。

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *

非簡單請求

前面有特別提到這是一項瀏覽器的機制用來阻擋非同源的請求,特別有些請求是只要送出就可能改變伺服器狀態像是:POSTPUTDELETE(或是任何非簡單請求),沒辦法單純的獲取請求後再決定要不要阻擋,因此這類請求會透過預檢請求(Preflight Request)也就是預先送出一個 OPTIONS 請求詢問伺服器是否允許操作:

OPTIONS /doc HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

然後伺服器回應允許該請求:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

確定該非簡單請求是可以被接受的後,瀏覽器才會真正發送請求:

POST /doc HTTP/1.1
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

並且伺服器回應允許的來源:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example

總結

CORS 是現代網頁應用中不可或缺的安全機制,能有效防止不同來源之間的不正當資源存取。透過 CORS,可以在保護用戶資料的同時,允許合法的跨來源請求。

延伸閱讀