XMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头

新手上路,请多包涵

tl;博士;关于同源策略

我有一个启动 express.js 服务器实例的 Grunt 进程。直到现在它开始提供空白页面,并且在 Chrome(最新版本)的开发人员控制台中的错误日志中显示以下内容时,这一直工作得非常好:

XMLHttpRequest 无法加载 https://www.example.com/

请求中不存在“Access-Control-Allow-Origin”标头

资源。因此,不允许访问源“ http://localhost:4300 ”。

是什么阻止我访问该页面?

原文由 Peter David Carter 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

tl;dr - 答案的末尾和标题有一个摘要,以便更容易找到相关部分。建议阅读所有内容,因为它为理解 原因 提供了有用的背景,这使得了解 如何 在不同情况下更容易应用。

关于同源策略

这是 同源策略。它是浏览器实现的安全功能。

您的特殊情况显示了它是如何为 XMLHttpRequest 实现的(如果您使用 fetch,您将获得相同的结果),但它也适用于其他事物(例如加载到 的图像或加载到 <iframe> <canvas> 的文档 <iframe> ),只是实现略有不同。

(奇怪的是,它也适用于 CSS 字体,但那是因为找到的代工厂坚持 DRM,而不是同源政策通常涵盖的安全问题)。

证明需要 SOP 的标准场景可以用 三个字符 来证明:

  • 爱丽丝是一个拥有网络浏览器的人

  • Bob 运行一个网站(在您的示例中为 https://www.[website].com/

  • Mallory 运行一个网站(在您的示例中为 http://localhost:4300

Alice 登录到 Bob 的站点,并在那里有一些机密数据。可能是公司内部网(只能通过 LAN 上的浏览器访问),或者是她的网上银行(只能通过输入用户名和密码后获得的 cookie 访问)。

Alice 访问了 Mallory 的网站,该网站有一些 JavaScript,导致 Alice 的浏览器向 Bob 的网站发出 HTTP 请求(从她的 IP 地址和她的 cookie 等)。这可以像使用 XMLHttpRequest 和读取 responseText 一样简单。

浏览器的同源策略阻止 JavaScript 读取 Bob 的网站返回的数据(Bob 和 Alice 不希望 Mallory 访问)。 (请注意,例如,您可以使用 <img> 元素跨源显示图像,因为图像的内容不会暴露给 JavaScript(或 Mallory)……除非您将画布放入混合中,在这种情况下您 生成一个同源违规错误)。


为什么在您认为不应该适用同源政策时适用

对于任何给定的 URL,可能不需要 SOP。出现这种情况的几个常见场景是:

  • 爱丽丝、鲍勃和马洛里是同一个人。

  • Bob 提供完全公开的信息

…但是浏览器无法知道上述任何一个是否正确,因此信任不是自动的,而是应用了 SOP。在浏览器将其提供给不同网站的数据之前,必须明确授予权限。


为什么同源策略仅适用于网页中的 JavaScript

浏览器扩展 * 、浏览器开发工具中的网络选项卡和 Postman 等应用程序都是已安装的软件。他们不会 仅仅因为您访问了不同 的网站而将数据从一个网站传递到属于不同网站的 JavaScript。安装软件通常需要更谨慎的选择。

没有第三方(马洛里)被认为是有风险的。

* 浏览器扩展确实需要仔细编写以避免跨域问题。 例如,请参阅 Chrome 文档


为什么用JS不用读取就可以在页面中显示数据

在许多情况下,Mallory 的站点会导致浏览器从第三方获取数据并显示(例如,通过添加 <img> 元素来显示图像)。但是,Mallory 的 JavaScript 不可能读取该资源中的数据,只有 Alice 的浏览器和 Bob 的服务器可以做到这一点,所以它仍然是安全的。


CORS

错误消息中提到的 Access-Control-Allow-Origin HTTP 响应 标头是 CORS 标准的一部分,该标准允许 Bob 明确授予 Mallory 的站点通过 Alice 的浏览器访问数据的权限。

一个基本的实现将只包括:

 Access-Control-Allow-Origin: *

…在响应标头中以允许任何网站读取数据。

 Access-Control-Allow-Origin: http://example.com

… 将只允许特定站点访问它,并且 Bob 可以根据 Origin 请求 标头动态生成它,以允许多个(但不是所有)站点访问它。

Bob 如何设置响应头的细节取决于 Bob 的 HTTP 服务器和/或服务器端编程语言。 Node.js/Express.js 的用户应该使用 文档齐全的 CORS 中间件。其他平台的用户应查看此 指南集合,了解可能有帮助的各种常见配置

应用 CORS 规则的模型

注意:有些请求很复杂,会发送一个 预检 OPTIONS 请求,服务器必须先响应该请求,然后浏览器才会发送 JS 想要发出的 GET/POST/PUT/Whatever 请求。仅将 Access-Control-Allow-Origin 添加到特定 URL 的 CORS 实现通常会因此而受阻。


显然,通过 CORS 授予权限是 Bob 仅在以下情况下才会做的事情:

  • 数据不是私人的

  • 马洛里是值得信赖的


如何添加这些标题?

这取决于您的服务器端环境。

如果可以,请使用旨在处理 CORS 的库,因为它们会为您提供简单的选项,而不必手动处理所有内容。

Enable-Cors.org 有一个您可能会发现有用的特定平台和框架的文档列表。

但我不是鲍勃!

Mallory 没有添加此标头的标准机制,因为它必须来自 Bob 的网站,而她无法控制该网站。

如果 Bob 正在运行一个公共 API,那么可能有一种机制可以打开 CORS(可能通过以某种方式格式化请求,或者在登录到 Bob 站点的 Developer Portal 站点后的配置选项)。不过,这必须是 Bob 实现的一种机制。 Mallory 可以阅读 Bob 网站上的文档以查看是否有可用的内容,或者她可以与 Bob 交谈并要求他实施 CORS。


提及“预检响应”的错误消息

一些跨源请求是 预检 的。

当(粗略地说)您尝试发出跨域请求时,就会发生这种情况:

  • 包括 cookie 等凭据

  • 无法使用常规 HTML 表单生成(例如,具有您无法在表单的 enctype 中使用的自定义标题或 Content-Type )。

如果您正确地做需要预检的事情

在这些情况下 ,此答案的其余部分仍然适用,但您还需要确保服务器可以侦听预检请求(这将是 OPTIONS (而不是 GETPOST 或您尝试发送的任何内容)并响应它使用正确的 Access-Control-Allow-Origin 标头以及 Access-Control-Allow-MethodsAccess-Control-Allow-Headers 以允许您的特定 HTTP 方法或标头。

如果您错误地触发了预检

有时人们在尝试构建 Ajax 请求时会出错,有时这些会触发预检的需要。如果 API 旨在允许跨域请求,但不需要任何需要预检的东西,那么这可能会中断访问。

触发此问题的常见错误包括:

  • 尝试将 Access-Control-Allow-Origin 和其他 CORS 响应标头放在请求上。这些不属于请求,不做任何有用的事情(您可以授予自己权限的权限系统有什么意义?),并且必须仅出现在响应中。

  • 试图在没有请求正文的 GET 请求上放置一个 Content-Type: application/json 标头来描述其内容(通常当作者混淆 Content-TypeAccept 时)。

在这两种情况下,删除额外的请求标头通常足以避免需要预检(这将解决与支持简单请求但不支持预检请求的 API 通信时的问题)。


不透明响应( no-cors 模式)

有时您需要发出 HTTP 请求,但不需要读取响应。例如,如果您将日志消息发布到服务器进行记录。

如果您使用 的是 fetch API (而不是 XMLHttpRequest ),那么您可以将其配置为不尝试使用 CORS。

请注意, 这不会让您做任何您需要 CORS 做的事情您将无法阅读回复。您将无法提出需要预检的请求。

它会让你发出一个简单的请求,看不到响应,也不会在开发者控制台中填满错误消息。

当您使用 fetch 发出请求并且没有获得使用 CORS 查看响应的权限时给出的 Chrome 错误消息解释了如何执行此操作:

CORS 策略已阻止从源“ https://example.net ”获取“ https://example.com/ ”的访问权限:请求的资源上不存在“ Access-Control-Allow-Origin ”标头。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。

因此:

 fetch("http://example.com", { mode: "no-cors" });


CORS 的替代品

JSONP

Bob 还可以使用 JSONP 之类的 hack 提供数据,这就是人们在 CORS 出现之前进行跨域 Ajax 的方式。

它通过以 JavaScript 程序的形式呈现数据来工作,该程序将数据注入 Mallory 的页面。

它要求 Mallory 信任 Bob 不会提供恶意代码。

注意共同的主题:提供数据的站点必须告诉浏览器第三方站点可以访问它发送到浏览器的数据。

由于 JSONP 通过附加 <script> 元素以调用页面中已有函数的 JavaScript 程序的形式加载数据,因此尝试在返回 JSON 的 URL 上使用 JSONP 技术将失败——通常会出现 CORB 错误— 因为 JSON 不是 JavaScript。

将两个资源移动到一个 Origin

如果运行 JS 的 HTML 文档和请求的 URL 位于同一来源(共享相同的方案、主机名和端口),则它们的同源策略默认授予权限。不需要 CORS。

代理

Mallory 可以 使用服务器端代码来获取数据(然后她可以像往常一样通过 HTTP 将数据从她的服务器传递到 Alice 的浏览器)。

它将:

  • 添加 CORS 标头

  • 将响应转换为 JSONP

  • 与 HTML 文档同源

该服务器端代码可以由第三方(例如 CORS Anywhere)编写和托管。请注意这对隐私的影响:第三方可以监控谁在他们的服务器上代理了什么。

Bob 不需要为此授予任何权限。

这里没有安全隐患,因为那只是在 Mallory 和 Bob 之间。 Bob 无法认为 Mallory 就是 Alice,也无法向 Mallory 提供 Alice 和 Bob 之间应该保密的数据。

因此,Mallory 只能使用这种技术来读取 公共 数据。

但是请注意,从其他人的网站上获取内容并自行显示可能会侵犯 _版权_,并让您面临法律诉讼。

编写网络应用程序以外的东西

如“为什么同源策略仅适用于网页中的 JavaScript”一节中所述,您可以通过不在网页中编写 JavaScript 来避免 SOP。

这并不意味着您不能继续使用 JavaScript 和 HTML,但您可以使用其他一些机制来分发它,例如 Node-WebKit 或 PhoneGap。

浏览器扩展

在应用同源策略之前,浏览器扩展可以在响应中注入 CORS 标头。

这些对开发很有用,但对生产站点不实用(要求站点的每个用户安装禁用浏览器安全功能的浏览器扩展是不合理的)。

它们也倾向于只处理简单的请求(在处理预检 OPTIONS 请求时失败)。

拥有适当的开发环境和本地开发 服务器

通常是更好的方法。


其他安全风险

请注意,SOP / CORS 不能缓解需要独立处理的 XSSCSRFSQL 注入 攻击。


概括

  • 无法在客户端代码中执行任何操作来启用 CORS 访问其他 人的 服务器。

  • 如果您控制正在向其发出请求的服务器:向其添加 CORS 权限。

  • 如果您对控制它的人友好:让他们为其添加 CORS 权限。

  • 如果是公共服务:

  • 阅读他们的 API 文档,了解他们对使用客户端 JavaScript 访问它的看法:

  • 他们可能会告诉您使用特定的 URL

  • 他们可能支持 JSONP

  • 它们可能根本不支持从客户端代码进行跨域访问(这可能是出于安全考虑而深思熟虑的决定,尤其是在您必须在每个请求中传递个性化 API 密钥的情况下)。

  • 确保您没有触发不需要的预检请求。 API 可能会授予简单请求的权限,但不会授予预检请求的权限。

  • 如果以上都不适用:让浏览器改为与 您的 服务器通信,然后让您的服务器从另一台服务器获取数据并将其传递。 (还有第三方托管服务将 CORS 标头附加到您可以使用的可公开访问的资源上)。

原文由 Quentin 发布,翻译遵循 CC BY-SA 4.0 许可协议

目标服务器必须允许跨源请求。为了允许它通过 express,只需处理 http options 请求:

 app.options('/url...', function(req, res, next){
   res.header('Access-Control-Allow-Origin', "*");
   res.header('Access-Control-Allow-Methods', 'POST');
   res.header("Access-Control-Allow-Headers", "accept, content-type");
   res.header("Access-Control-Max-Age", "1728000");
   return res.sendStatus(200);
});

原文由 Daphoque 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题