2024/07/29

CORS

同源政策 Same-origin policy 是在Web瀏覽器中,允許某個網頁指令碼也就是 javascript 訪問另一個網頁的資料的前提是,這兩個網頁必須有相同的URI、主機名和埠號,一旦兩個網站滿足上述條件,這兩個網站就被認定為具有相同來源。在 same origin policy 的限制下,能限制瀏覽器防止某個網頁上的惡意指令碼通過該頁面的文件物件模型存取另一網頁上的敏感資料。

滿足同源的條件有三個

  1. 使用相同的通訊協定 http/https

  2. 有相同的網域 domain

  3. 有相同的 port

跨來源請求 cross-origin http request,就是在非同源的情況下,發生的 http request,在發生這種狀況時,必須要遵守 CORS (Cross-Origin Resource Sharing) 的規範

定義

CORS 就是針對非同源的 http request 制定的規範,當 javascript 存取非同源的資源時, server 必須明確告知瀏覽器,是否允許這樣的 request,只有 server 允許的 request 能夠被瀏覽器發送,否則就會失敗

CORS 的跨來源請求有兩種:「簡單」與「非簡單」

跨來源請求

「簡單」跨來源請求有兩個條件

  1. 只能使用 GET, POST, HEAD method

  2. http reques header 只能有 AcceptAccept-LanguageContent-Language 或 Content-Type,且 Content-Type 只能是 application/x-www-form-urlencodedmultipart/form-data 或 text/plain

只要不是「簡單」跨來源請求,就是「非簡單」跨來源請求

簡單請求 simple requests 不會觸發 CORS 預檢 preflighted

sample

以下是一個 cross-origin request/response,可發現在 request 裡面,會有一個 Origin http header

Origin 標記,這是來自 http://foo.example 的 cross-orign request

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[XML Data]

下面的 response 有 Access-Control-Allow-Origin 這個 header,* 表示server 允許來自任意 origin 的 http request

預檢 preflighted request

「非簡單」request,在瀏覽器真正發送 request 之前,會先發送一個 preflight request,用途是詢問 server 是否允許這樣的 request

preflighted request 是用 http OPTIONS method 產生的

在 request 會有這兩個 header

  1. Access-Control-Request-Method:該 request 是哪一個 http method

  2. Access-Control-Request-Headers:該 request 裡面待有哪些非簡單的 http header

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
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
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

response 有以下重點

  1. Access-Control-Allow-Origin:允許的 origin

  2. Access-Control-Allow-Methods:允許的跨 origin http method

  3. Access-Control-Allow-Headers:允許使用的 http header

  4. Access-Control-Max-Age:本次預檢請求回應所可以快取的秒數,代表 browser 可 cache 這個 response 多久

cookie

通常 CORS 不允許使用 cookie,也不支援 redirect

如果真的還是要使用 cookie

fetch API 要加上 credentials

fetch('https://othersite.com/data', {
  credentials: 'include'
})

XMLHttpRequest 要加上 withCredentials = true

const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open('POST', 'https://othersite.com/data');

server 端也要增加一個 response header

Access-Control-Allow-Credentials: true

使用 cookie 時,Access-Control-Allow-Origin 不能直接填寫 *

必須明確標示哪些 origin 可以使用 cookie

References

跨來源資源共用(CORS) - HTTP | MDN

[教學] 深入了解 CORS (跨來源資源共用): 如何正確設定 CORS?

沒有留言:

張貼留言