关于 CORS 你应该知道什么 CORS 是什么,以及它不是什么 CORS 的工作原理。它在浏览器中的样子 CORS 的棘手之处

2025-05-25

关于 CORS 你应该知道的事

CORS 是什么,以及它不是什么

CORS 如何工作。

在浏览器中的显示效果

关于 CORS 的棘手问题

如果你和我一样,第一次遇到 CORS(跨域资源共享)时,你唯一想做的就是让你的服务器接受那些该死的 Ajax 请求,然后就完事了。于是你去 Stack Overflow,复制粘贴了一段代码来设置一些 headers,结果就成功了。

然而,您可能想知道一些事情。

CORS 是什么,以及它不是什么

这常常会让新手感到困惑,因为他们无法立即理解 CORS 究竟能实现什么。首先,CORS 本身并不是一种安全措施,恰恰相反:CORS 是一种规避“同源策略”的方法,而“同源策略”是一种阻止你向不同域名发出 Ajax 请求的安全措施。

同源策略规定,一个域名下的网站不能向另一个域名发出 XHR 请求。这可以防止恶意网站向已知网站(例如 Facebook 或 Google)发出请求,希望用户已登录,从而冒充用户。此策略
由浏览器实现(所有浏览器都实现了同源策略,尽管略有不同),这意味着它不适用于来自服务器或任何其他 HTTP 客户端(例如 cURL 或 POSTman)的请求。此外,服务器对此完全没有控制权:它会将每个请求都视为来自受信任域,是否阻止请求完全由浏览器决定。

SOP 并非旨在阻止攻击者向您的服务器发出请求(因为攻击者显然不会使用浏览器)。它仅旨在防止使用信誉良好的浏览器的合法用户在不知情的情况下向您的网站发出请求。

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

CORS 如何工作。

CORS 和 HTTP 的其他部分一样,本质上是浏览器和服务器之间的对话。假设你的前端在 domain-a.com 上,而你的 API 在 Domain-b.com 上,那么它会像这样:

  -浏览器: “嘿,域名B,域名A.com上的这个脚本要求我向你发送一个Ajax请求,但我应该阻止它,除非你允许。”
  -服务器: “我不知道,但我可以告诉你,只https://domain-a.com允许发送GET、POST、OPTIONS和DELETE请求,并且每10分钟需要验证一次。
  浏览器心想:“是的,这是正确的域名,我会发送请求!”
  -浏览器: “嘿,域名B,我想在这个端点上发送POST请求。”
  -服务器:好的,这是200

或者,如果用户位于不同的域,对话将会更短,如下所示:

  -浏览器: “嘿,domain-b.com,malicious-domain.com 上的这个脚本要求我向你发起一个 AJAX 查询,但我应该阻止它,除非你允许。”
  -服务器: “我不知道,但我可以告诉你,只https://domain-a.com允许发送 GET、POST、OPTIONS 和 DELETE 请求,而且每 10 分钟需要验证一次。”
  浏览器心想:“哦,这不是正确的域名,我们最好不要发送这个请求”,然后在控制台中发送错误信息。

在浏览器中的显示效果

在我上面的小场景中,浏览器发出的第一个问题称为预检请求,对应的 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


Enter fullscreen mode Exit fullscreen mode

它告诉浏览器,只有来自 domain-A.com 的请求才能执行,它只能发出GETPOST请求(例如OPTIONS请求会被阻止),并且它可以将此信息缓存 3600 秒,因此它不需要每次都发出新的请求。DELETEPUTOPTIONS

当然,如果我们在不同的域名上,那就行不通了。浏览器会发送OPTIONS请求,在控制台中抛出一个错误,并且永远不会发送POST请求:

很简单,对吧?

嗯,是的,除了有几个陷阱……

关于 CORS 的棘手问题

所有响应都应包含 CORS 标头

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

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

有些请求不会触发预检请求,例如 GET 请求,或Content-Type标头设置为 的 POST 请求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");
};


Enter fullscreen mode Exit fullscreen mode

或者在 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();
});

Enter fullscreen mode Exit fullscreen mode




同源策略适用于 Chrome 和 Safari 上的文件系统,而不适用于 Firefox。

如果您向本地文件发出请求,Firefox 会认为该文件始终位于同一域中并允许该请求。基于 Webkit 的浏览器(例如 Chrome 或 Safari)会将此视为安全风险,并阻止对本地文件的 Ajax 查询。解决这个问题的唯一方法是使用 Firefox,或者安装一个可以发送Access-Control-Allow-Origin: *标头的 Web 服务器。正如@brianjenkins94在评论中
指出的那样,您也可以在 Chrome 中启用该标志。--disable-web-security

iOS WKWebview 需要 CORS

如果您正在开发使用 WebView(使用 Cordova 或 Ionic)的移动应用,Android 系统不会给您带来任何麻烦,但 iOS 系统上新的 WKWebview 需要 CORS。这意味着您几乎必须始终将Access-Control-Allow-Origin标头设置为*,这确实不太理想。
另一种选择是,不在应用中发出 Ajax 请求,而是使用 Cordova 插件发出原生 HTTP 请求,这样可以顺利地忽略同源策略。

感谢阅读!
如果您想了解 CORS 的更深入描述,请访问 MDN:https://developer.mozilla.org/docs/Web/HTTP/CORS

文章来源:https://dev.to/nicolus/what-you-should-know-about-cors-48d6
PREV
CDK 模式 20 周年!让我们一起探索 AWS 的 20 个无服务器模式
NEXT
¿Cómo integrar Mercado Pago 与您的网络吗?需要注意的事项 建立规则 创建控制器 创建服务创建