最近實在太忙碌,今天有點時間把以前實作過 Cross-Origin Resource Sharing (CORS) 的經驗寫下來,在 Web 之間進行串接溝通已經是很稀鬆平常的事情,最一般的狀況是 Server 跟 Server 之間的溝通,這通常沒什麼問題,但是如果 Request 發生在 User Browser 端的時候就會遇到 「Cross Domain Access (跨網域存取)」的問題。
這種狀況是因為瀏覽器因為資料安全的因素,替使用者做的安全機制,避免資料被傳送到其他 Web Site (不同網域),所以只要使用者的行為有「誇網域」行為的話就會被阻擋。
舉例我有一個 A 網站要傳送 Request 到 B 網站,而 A(a.com) 和 B(b.com) 的網域不同,那麼在 Chrome 的開發者工具則可以看到
XMLHttpRequest cannot load http://b.com Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://a.com’ is therefore not allowed access.
這就是被瀏覽器阻擋。
但是實務上還是會有跨網域存取的必要,只要在適當信任範圍內就不會造成 CORS 的問題,所以 W3C 就提供了「Cross-Origin Resource Sharing」的方法,利用 Access-Control-* 系列的 Header 來 Trust Cross Origin 的 Request。
- Access-Control-Allow-Origin (Response Header)
- Access-Control-Allow-Credentials (Response Header)
- Access-Control-Expose-Headers (Response Header)
- Access-Control-Max-Age (Response Header)
- Access-Control-Allow-Methods (Response Header)
- Access-Control-Allow-Headers (Response Header)
- Origin (Request Header)
- Access-Control-Request-Method (Request Header)
- Access-Control-Request-Headers (Request Header)
特別注意的是有些 Header 是 Response 用,有些是 Request 使用
Access-Control-Allow-Origin
Access-Control-Allow-Origin 這個 Header 是 CORS 必要使用的,這個 Header 可以讓 Brower 信任來源端的 Domain,
在 Nginx 上可以用 add_header 實作,並且加上一些「君子式」的針對副檔名過濾。
server { ... if ($request_filename ~* \.(js|eot|otf|ttf|woff)$ ) { add_header 'Access-Control-Allow-Origin: example.com'; } ... }
但是在使用 Access-Control-Allow-Origin 時要非常小心,如果有許多網站要存取的話可以直接使用 Access-Control-Allow-Origin *,但如果資料過於敏感 (如帳號密碼) 就萬萬不可這樣做,務必要經過白名單的過濾才行。
Access-Control-Allow-Methods
這個 Header 是用來處理特殊的 Request 動作,一般性的 Method 都在預設 (根據 Section 版本的更新有所異動),如:
- OPTIONS
- GET
- HEAD
- POST
- PUT
- DELETE
- TRACE
- CONNECT
- extension-method
在 Nginx 可以這樣加入其他 Method
server { ... add_header 'Access-Control-Allow-Methods: POST'; ... }
Access-Control-Allow-Headers
Access-Control-Allow-Headers 這個 Header 在 CORS 也算常用,如果你有自訂一些 Header 或是 Authorization 的話就要透過 Access-Control-Allow-Headers 來允許。
一般你可能在 Browers 看到這類型的錯誤:
Request header Authorization field is not allowed by Access-Control-Allow-Headers in preflight response
這代表 Authorization 不在 Access-Control-Allow-Headers 的允許範圍內,所以被阻擋
在 Nginx 加入 Access-Control-Allow-Headers 信任 Authorization Header。
server { ... add_header 'Access-Control-Allow-Headers: Authorization'; ... }
以上是一些常用的 CORS 實作,如文中所述 CORS 如果沒弄好很容易會被駭客利用跨網站竊取到使用者資料,所以務必小心!