【译】你应该了解的 CORS

原文: What you should know about CORS
做者:Nicolas Bailly
译者:博轩

图片描述

若是你和我同样,第一次遇到 CORS (跨域资源共享),你想让服务器接收那些你拼接的 Ajax 请求并处理他们。因此你去 stackoverflow.com 复制一段代码来设置一些 HTTP Headers,让请求能够正常工做。php

可是,可能还有一些事情你应该知道。前端

CORS 是什么,不是什么

新手一般混淆的缘由,就是由于他们并不明确 CORS 能作什么。首先,CORS 并非一种安全措施,实际上偏偏相反:CORS 是一种绕过“同源策略(SOP)”的方法。同源策略是一种安全措施,阻止您向其余域发出Ajax请求。node

同源策略声明一个域上的网站,没法向另外一个域发出 XMLHttpRequest(XHR) 请求。这能够防止恶意网站向已知网站(好比 Facebook 或者 Google)发出请求,改变用户的登陆状态,以即可以冒充其余用户。此策略由浏览器实现(全部浏览器都实现了同源策略,尽管实现细节上存在细微的差异),这意味着此策略并不适用于从服务器,或者任何其余HTTP客户端(好比 cURLpostman)发出的请求。此外,服务器一样没法彻底控制它:服务器将处理每一个请求,并假设他们都来自可信域,请求是否会被阻止彻底取决于浏览器。web

同源策略毫不意味着防止攻击者向您的服务器发出请求(由于攻击者显然不会使用浏览器)。它只是为了防止合法用户在使用知名浏览器浏览网站时,在不知情的状况下,向你的网站发起请求。json

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:
developer.mozilla.org...

本文已经联系原文做者,并受权翻译,转载请保留原文连接
相关文章
相关标签/搜索