【譯】你應該瞭解的 CORS

原文:What you should know about CORS
作者:Nicolas Bailly
譯者:博軒

圖片描述

如果你和我一樣,第一次遇到 CORS (跨域資源共享),你想讓服務器接收那些你拼接的 Ajax 請求並處理他們。所以你去 stackoverflow.com 複製一段代碼來設置一些 HTTP Headers,讓請求可以正常工作。

但是,可能還有一些事情你應該知道。

CORS 是什麼,不是什麼

新手通常混淆的原因,就是因爲他們並不明確 CORS 能做什麼。首先,CORS 並不是一種安全措施,實際上恰恰相反:CORS 是一種繞過“同源策略(SOP)”的方法。同源策略是一種安全措施,阻止您向其他域發出Ajax請求。

同源策略聲明一個域上的網站,無法向另一個域發出 XMLHttpRequest(XHR) 請求。這可以防止惡意網站向已知網站(比如 Facebook 或者 Google)發出請求,改變用戶的登錄狀態,以便可以冒充其他用戶。此策略由瀏覽器實現(所有瀏覽器都實現了同源策略,儘管實現細節上存在細微的差別),這意味着此策略並不適用於從服務器,或者任何其他HTTP客戶端(比如 cURLpostman)發出的請求。此外,服務器同樣無法完全控制它:服務器將處理每個請求,並假設他們都來自可信域,請求是否會被阻止完全取決於瀏覽器。

同源策略絕不意味着防止攻擊者向您的服務器發出請求(因爲攻擊者顯然不會使用瀏覽器)。它只是爲了防止合法用戶在使用知名瀏覽器瀏覽網站時,在不知情的情況下,向你的網站發起請求。

CORS 是一種繞過同源策略的方法,在某些情況下,您希望一些特殊的站點可以向你的服務器發起請求,即使正常情況下它會被阻止。(通常,是允許您的前端應用向您的API發出請求)。

CORS 是如何工作的

HTTP的相同,CORS基本上也是瀏覽器和服務器之間的對話。假設你前端的域名爲 domain-a.com ,後端API的域名爲 domain-b.com,對話會是這樣的:

  • 瀏覽器:“Hey domain-b.comdomain-a.com上的這個腳本要向你發起一次Ajax請求,但是我應該阻止它,除非你告訴我這個請求是沒問題的。”
  • 服務器:“我不知道,但是我可以告訴你,https://domain-a.com 只允許發送 GET,POST,OPTIONS 和 DELETE 請求,並且需要每10分鐘驗證一次。”
瀏覽器想了想:“ yeah,這是個正確的域名,我應該給他發送請求。”
  • 瀏覽器:“Hey domain-b.com,我想在這個終端向你發送 POST 請求。”
  • 服務器:“沒問題,這是你的 200

或者,如果用戶位於不同的域,則對話會更短:

  • 瀏覽器:“Hey domain-b.commalicious-domain.com(惡意站點)上的這個腳本要向你發起一次Ajax請求,但是我應該阻止它,除非你告訴我這個請求是沒問題的。”
  • 服務器:“我不知道,但是我可以告訴你,https://domain-a.com 只允許發送 GET,POST,OPTIONS 和 DELETE 請求,並且需要每10分鐘驗證一次。”
瀏覽器想了想:“ Oh,這不是正確的域名,我們最好不要發送請求”,然後在控制檯打印了錯誤。
譯註:第二種,使用開發者工具查看時,看不到 Response Headers,但是可以看到下圖中的提示:

圖片描述

Node CORS 測試地址

在瀏覽器中的樣子

在上面的小場景中,瀏覽器提出的第一個問題稱爲 預檢請求,對應的 HTTP 謂詞是 OPTIONS。遇到這種預檢請求,服務器應該總是返回一個 200 的響應,沒有正文,但是會包含 Access-Control-Allow-Origin ,以及一些其他響應頭。在我們的示例中響應頭如下:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://domain-a.com
Access-Control-Allow-Methods: GET, POST, OPTIONS, DELETE
Access-Control-Max-Age: 3600

它告訴瀏覽器,它只能響應來自 domain-a.com 的請求,可以處理 GET, POST, OPTIONS 或者 DELETE 請求(PUT 請求會被阻止),並且他可以緩存此信息 3600 秒,所以它不需要都發起一個新的 OPTIONS 請求。

圖片描述

當然,如果我們使用其他域名,這將不起作用。瀏覽器會發送 OPTIONS 請求,然後在控制檯中拋出異常,並且永遠不會發送 POST 請求。

圖片描述

很直接,對吧?

但是,也存在一些陷阱...

關於 CORS 的棘手問題

所求請求都包含 CORS 頭(headers

您可能會認爲,如果您的服務器響應 OPTIONS 請求時返回 200,然後你將這些正確的響應頭去掉。然後你將看到瀏覽器先發送了 OPTIONS 請求,然後發送了其他請求,其他請求掛掉了... 這是因爲每個請求(GET, POST, 或者其他請求)都應該包含相同的響應頭:“Access-Control-Allow-Headers”。

並非所有請求都會觸發預檢請求

有一些請求不會觸發預檢請求,比如 GET 請求,或者 Content-Type 設置爲 application/x-www-form-urlencoded 的 POST 請求。這些是瀏覽器一直允許的“簡單請求”,(即使在CORS不支持的情況下,你依然可以使用超鏈接(a標籤)或者使用 POST 請求向其他網站提供表單,您可以在此處找到完整列表。在 POST 請求的情況下,結果會有些違反直覺:瀏覽器將發出 POST 請求(因此您的服務器可能會保留一些數據),然後忽略響應。

在傳統的Web應用程序中,您可以使用 application/json 作爲 content-type,因此會有預檢請求,但請記住,您的服務器可能仍會收到來自其他域的 POST 請求,因此請不要盲目接受它們。

被允許的域名必須包含協議

您不能只將 mydomain.com 當做域名使用,它還需要包含協議,(例如:https://mydomain.com)。有趣的是,你不能同時接收 httphttps ,因爲......

您只能允許一個域

您可以使用 Access-Control-Allow-Origin: * 來允許每個域訪問,也可以只允許一個域訪問。這意味着如果您需要多個域來訪問您的API時,您需要自己處理它。

處理此問題的最簡單方法是在服務器上維護允許訪問的域列表,如果域位於該列表中,則動態的改變響應頭的內容。下面是一個 PHP 的例子:

$allowedDomains = [
    "http://www.mydomain.com",
    "https://www.mydomain.com",
    "http://www.myotherdomain.com",
    "http://www.myotherdomain.com",
];

$originDomain = $_SERVER['HTTP_ORIGIN'];

if (in_array($originDomain, $allowedDomains)) {
    header("Access-Control-Allow-Origin: $originDomain");
};

或者 Node.js 的例子(改編自這個 stackoverflow 答案

app.use(function(req, res, next) {
  const allowedOrigins = [
    "http://www.mydomain.com",
    "https://www.mydomain.com",
    "http://www.myotherdomain.com",
    "http://www.myotherdomain.com",
  ];
  const origin = req.headers.origin;
  if(allowedOrigins.indexOf(origin) > -1){
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  return next();
});
同源策略適用於 Chrome 和 Safari 的文件系統,不適用於 Firefox 的

如果您向本地文件發出請求,Firefox會認爲它始終位於同一個域上並允許該請求。基於 Webkit 的瀏覽器(如Chrome或Safari)會將此視爲安全風險,並阻止對本地文件的Ajax查詢。解決這個問題的唯一方法是使用Firefox,或安裝將發送 Access-Control-Allow-Origin: * 響應頭的Web服務器。
正如 @brianjenkins94 在評論中指出的那樣,您也可以用 --disable-web-security 參數來啓動Chrome 。

iOS WKWebview需要CORS

如果您正在開發使用 webview(使用Cordova或Ionic)的移動應用程序,Android將不會給您帶來任何麻煩,但iOS上的新 WKWebview 將需要CORS。這意味着您幾乎必須始終將 Access-Control-Allow-Origin 標頭設置爲 * ,但實際上這並不理想。
另一個選擇是不在您的應用程序中發出Ajax請求並使用 cordova 插件來生成本機 http 請求,這將很方便的繞過同源策略。

謝謝閱讀 !
如果您想要更深入地瞭解CORS,請訪問MDN:
https://developer.mozilla.org...

本文已經聯繫原文作者,並授權翻譯,轉載請保留原文鏈接
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章