您应该了解的CORS

如果你和我一样,第一次遇到CORS(或跨域资源共享),你想要的只是服务器接受那些darn ajax请求并完成它。所以你去堆栈溢出,复制粘贴代码片段来设置一些标题,它工作。

但是,您可能想知道一些事情。

什么是CORS,什么不是

这通常是新手混淆的原因,因为它并不能立即明白CORS应该达到的目标。首先,CORS本身并不是一种安全措施,实际上恰恰相反:CORS是一种绕过“同源策略”的方法,这是一种安全措施,阻止您向其他域发出Ajax请求。

同源策略声明一个域上的网站无法向另一个域发出xhr请求。这可以防止恶意网站向已知网站(想想Facebook或Google)发出请求,希望用户已经登录,以便可以冒充他们。
此策略由浏览器实现(所有这些都实现SOP,尽管存在细微差别),这意味着它不适用于从服务器或任何其他HTTP客户端(如cURL或POSTman)发出的请求。此外,服务器完全无法控制它:它将处理每个请求,就好像它来自可信域,完全取决于浏览器阻止请求。

SOP绝不意味着防止攻击者向您的服务器发出请求(因为攻击者显然不会使用浏览器)。它只是为了防止使用信誉良好的浏览器的合法用户在不知情的情况下向您的网站发出请求。

现在,CORS是一种绕过SOP的方法,在某些情况下,您希望允许一个特定网站向您的服务器发出请求,即使它通常会被阻止。 (通常,允许您的前端应用向您的API发出请求)。

CORS如何工作。

与HTTP的其余部分一样,CORS基本上是浏览器和服务器之间的对话。假设你的前端是在domain-a.com上的domain-a.com和你的API,它会是这样的:

-Browser:“嘿Domain-B,Domain-A.com上的这个脚本要求我向你发一个ajax查询,但我应该阻止它,除非你告诉我它没问题。”
-Server:“我不知道,但我只能告诉你 https://domain-a.com 允许进行GET,POST,OPTIONS和DELETE请求,这需要每10分钟验证一次。
浏览器认为自己“是的,那是正确的域名,我会发送请求”
-Browser:“嘿domain-b,我想在这个端点上发帖。
-Server:当然,这是200

或者,如果用户位于不同的域,则对话会更短,看起来像这样:

-Browser:“嘿domain-b.com,恶意网站上的这个脚本要求我向你发一个ajax查询,但除非你告诉我没事,否则我应该阻止它。”
-Server:“我不知道,但我只能告诉你 https://domain-a.com 允许进行GET,POST,OPTIONS和DELETE请求,这需要每10分钟验证一次。
浏览器认为自己“哦,这不是正确的域名,我们最好不要发出请求”,然后继续在控制台中发送错误。

它在浏览器中的外观

在我上面的小场景中,浏览器中的第一个问题称为Preflight请求,相应的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,它只能进行 GETPOSTOPTIONS 要么 DELETE 请求(a PUT 请求将被阻止(例如),并且它可以缓存此信息3600秒,因此它不需要创建新的 OPTIONS 请求每一次。

当然,如果我们在一个不起作用的不同领域。浏览器会发送一个 OPTIONS 请求,在控制台中抛出错误,永远不发送 POST 要求:

非常直接,对吧?

是的,除了有一些陷阱……

关于CORS的棘手问题

所有响应都应包含CORS标头

您可能会认为,如果您的服务器以200和正确的标题回答OPTIONS请求,您将看到清除,并且您将看到浏览器发送OPTIONS请求,然后发送您的实际请求,然后悲惨地失败..那是因为每个请求(GET,POST或其他)都应该包含相同的“Access-Control-Allow-Headers”。

并非所有请求都会触发预检请求

有一些请求不会触发预检请求,例如GET请求,或带有a的POST请求 Content-Type 标头设置为 application/x-www-form-urlencoded。那些是浏览器一直允许的“简单请求”(即使在CORS完成之前,您始终能够将表单链接或POST到不同的网站),您可以在此处找到完整列表。
在POST请求的情况下,结果有点违反直觉:浏览器将发出POST请求(因此您的服务器可能会保留一些数据),然后忽略响应

在您使用的传统Web应用程序中 application/json 作为内容类型,因此会有预检请求,但请记住,您的服务器可能仍会收到来自其他域的POST请求,因此请不要盲目接受它们。

允许的域必须包含协议

你不能只是放 mydomain.com 作为域,它需要包括协议(例如。 https://mydomain.com)。有趣的是你不能真正接受http和https,因为……

您只能允许一个域

您可以允许每个域 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中(改编自这个SO anwser)

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,或安装将发送的Web服务器 Access-Control-Allow-Origin: * 头。

iOS WKWebview需要CORS

如果您正在开发使用webview(使用Cordova或Ionic)的移动应用程序,Android将不会给您带来任何麻烦,但iOS上的新WKWebview将需要CORS。这意味着你几乎必须始终设置 Access-Control-Allow-Origin 标题为 *,这真的不太理想。

谢谢阅读
如果您想要更深入地描述CORS,请访问MDN:https://developer.mozilla.org/docs/Web/HTTP/CORS。

资讯来源:由0x资讯编译自DEV,原文:https://dev.to/nicolus/what-you-should-know-about-cors-48d6 ,版权归作者所有,未经许可,不得转载
你可能还喜欢