XMLHttpRequest 无法加载 XXX 没有“访问控制-允许-起源”头

关于同一原产地政策

我有一个 Grunt 进程,它会启动 Express.js 服务器的一个实例。直到现在,这个功能一直运行得很好,直到它开始提供一个空白页面,在 Chrome (最新版本)的开发者控制台的错误日志中出现了以下内容:

XMLHttpRequest 无法加载 < a href = “ https://www.example.com/”rel = “ norefrer”> https://www.example.com/ 请求的标头上没有“访问控制-允许-起源”标头 因此,原产地「 http://localhost:4300」是不允许存取的。

是什么阻止我访问这个页面?

321877 次浏览

医生

最后有一个总结和标题的答案,以便更容易找到相关的部分。建议阅读所有内容,因为它为理解 为什么提供了有用的背景知识,使得了解 怎么做在不同情况下如何应用变得更加容易。

关于同一原产地政策

这是 同一原产地政策。它是由浏览器实现的安全特性。

您的特定案例展示了它是如何为 XMLHttpRequest 实现的(如果使用提取,您将得到相同的结果) ,但它也适用于其他事情(例如加载到 <canvas>的图像或加载到 <iframe>的文档) ,只是实现稍有不同。

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

展示 SOP 需求的标准场景可以用 三个字来演示:

  • 爱丽丝是一个有浏览器的人
  • Bob 运行一个网站(在您的示例中是 https://www.[website].com/)
  • Mallory 运行一个网站(例子中是 http://localhost:4300)

爱丽丝登陆了鲍勃的网站,里面有一些机密数据。也许是公司内部网(只有局域网上的浏览器才能访问) ,或者是她的网上银行(只能通过输入用户名和密码后得到的 cookie 访问)。

爱丽丝访问马洛里的网站,其中有一些 JavaScript,使爱丽丝的浏览器对鲍勃的网站发出 HTTP 请求(从她的 IP 地址与她的 cookie 等)。这可以像使用 XMLHttpRequest和读取 responseText一样简单。

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


为什么同一原产地政策在你认为不应该适用的时候却适用

对于任何给定的 URL,可能不需要 SOP。下面是一些常见的情况:

  • 爱丽丝,鲍勃和马洛里是同一个人。
  • 鲍勃提供了完全公开的信息

... 但是浏览器无法知道上述任何一个是否正确,因此信任不是自动的,SOP 是应用的。浏览器必须先获得明确的许可,才能将其提供给其他网站的数据显示出来。


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

浏览器扩展 *,浏览器开发工具和应用程序(如 Postman)中的网络选项卡是安装软件。它们不会将数据从一个网站传递给属于另一个网站 只是因为你访问了那个不同的网站的 JavaScript。安装软件通常需要更有意识的选择。

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

浏览器扩展需要仔细编写,以避免跨源问题。


为什么可以在页面中显示数据而不用使用 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 中间件。其他平台的用户应该看看这个 各种常见配置的导轨集合,它可能会有所帮助。

Model of where CORS rules are applied

注意: 有些请求很复杂,发送一个 起飞前选项请求,在浏览器发送 GET/POST/PUT/JS 想要发出的任何请求之前,服务器都必须响应这个请求。只向特定 URL 添加 Access-Control-Allow-Origin的 CORS 实现常常因此而出错。


显然,通过 CORS 授予许可只有在以下两种情况下 Bob 才会这么做:

  • 数据不是私有 或者
  • Mallory 是值得信任的

如何添加这些标题?

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

如果可以的话,使用一个设计用来处理 CORS 的库,因为它们将为您提供简单的选项,而不必手动处理所有事情。

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

但我不是鲍勃!

马洛里没有标准的机制来添加这个头,因为它必须来自 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 响应头放在请求上。这些不属于请求,不要做任何有用的事情(权限系统的意义是什么,您可以授予自己权限?),并且必须只出现在响应中。
  • 尝试将一个 Content-Type: application/json头放在一个没有请求主体来描述其内容的 GET 请求上(通常是当作者将 Content-TypeAccept混淆时)。

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


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

有时需要发出 HTTP 请求,但不需要读取响应。例如,如果你正在向服务器发送一条日志消息,以便进行记录。

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

请注意,这不会让你做任何你需要 CORS 做的事情.您将无法读取响应.You 将不能提出一个需要飞行前的请求。

它将允许您发出一个简单的请求,不会看到响应,并且不会在 Developer Console 中填充错误消息。

当你使用 fetch发出请求并且没有获得使用 CORS 查看响应的权限时,Chrome 错误消息会给出解释:

从原来的“ https://example.net”获取“ https://example.com/”的访问已被 CORS 策略阻止: 请求的资源上没有“ Access-Control-Allow-Origin”头。如果一个不透明的响应满足您的需求,将请求的模式设置为“ no-CORS”,以便在禁用 CORS 的情况下获取资源。

因此:

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

CORS 的替代品

JSONP

Bob 还可以使用像 JSONP这样的黑客技术提供数据,在 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 之间的事。鲍勃不可能认为马洛里就是爱丽丝,也不可能向马洛里提供应该在爱丽丝和鲍勃之间保密的数据。

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

但是,请注意,从别人的网站上获取内容并在自己的网站上展示可能是违反 版权的,这将使你面临法律诉讼。

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

正如在「为什么同一原产地政策只适用于网页中的 JavaScript 」一节中所指出的,你可以避免在网页中编写 JavaScript,从而避免使用标准作业程序。

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

浏览器扩展

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

这些对于开发是有用的,但是对于生产站点来说是不实用的(要求站点的每个用户安装一个禁用其浏览器安全特性的浏览器扩展是不合理的)。

它们也倾向于只处理简单的请求(在处理飞行前选项请求时失败)。

使用本地开发 < em > 服务器创建适当的开发环境 通常是更好的方法。


其他保安风险

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


摘要

  • 你的客户端代码中,您无法使 CORS 访问某人的 别人的服务器。
  • 如果您控制服务器,请求将被发送到: 向其添加 CORS 权限。
  • 如果您与控制它的人友好: 让他们添加 CORS 权限到它。
  • 如果是公共服务:
    • 阅读他们的 API 文档,看看他们是如何使用客户端 JavaScript 访问 API 的:
      • 他们可能会告诉你使用特定的 URL
      • 他们可能支持 JSONP
      • 它们可能根本不支持来自客户端代码的跨源访问(这可能是出于安全考虑的一个深思熟虑的决定,特别是如果您必须在每个请求中传递一个个性化的 API 密钥)。
    • 确保你没有触发不需要的飞行前请求。API 可以为简单请求授予权限,但不授予预先标记的请求。
  • 如果上述方法都不适用: 让浏览器与 你的服务器通信,然后让服务器从另一台服务器获取数据并传递给其他服务器。(还有一些第三方托管服务将 CORS 头附加到您可以使用的公共访问资源上)。

这是因为 CORS 错误而发生的。CORS 代表跨源资源共享。简单地说,当我们尝试从另一个域访问一个域/资源时,就会发生此错误。

点击这里阅读更多: Jquery 的 CORS 错误

为了解决这个问题,如果您可以访问其他域,那么必须允许服务器中存取-控制-允许-起源。这可以添加到标题中。您可以为所有请求/域或特定域启用此选项。

如何让跨来源资源共享(CORS)发布请求正常工作

这些链接可能会有帮助

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

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);
});

您应该启用 CORS 使其工作。

因为公认的答案中没有提到这一点。

  • 这不是这个问题的确切情况,但可能有助于其他人寻找这个问题
  • 这是您可以在客户机代码中执行的操作,以防止 有些案子中的 CORS 错误。

你可以利用 简单的要求
为了执行“简单请求”,请求需要满足几个条件。例如,只允许 POSTGETHEAD方法,以及只允许一些给定的头(你可以找到所有的条件 给你)。

如果你的客户端代码没有在请求中明确设置受影响的 Header (例如“ Accept”)的修复值,那么 也许吧就会发生一些客户端确实自动设置了这些 Header 的一些“非标准”值,导致服务器不接受它作为简单请求——这会给你一个 CORS 错误。

这个 CORS 问题没有进一步阐述(为其他原因)。

我现在有这个问题是因为不同的原因。 我的前端返回’访问控制-允许-起源’头错误。

只是我指向了错误的 URL,所以这个标题没有被正确反映(我一直假设它反映了)。Localhost (前端)-> 调用不安全的 http (应该是 https) ,确保前端的 API 端点指向正确的协议。

“获取”请求并附加头转换为“选项”请求。因此,Cors 出现了政策问题。您必须实现“选项”请求到您的服务器。用于 Nodejs 服务器: 详情

app.use(cors)

用于 Java 与 Angular: 详情集成

@CrossOrigin(origins = "http://localhost:4200")

我在 Chrome 控制台中得到了同样的错误。

我的问题是,我试图去网站使用 http://而不是 https://。所以没有什么需要修复的,只需要使用 https到同一个站点。

这个窃听器花了我两天时间。我检查了我的服务器日志,浏览器 Chrome/Edge 和服务器之间的预飞行选项请求/响应是正常的。主要原因是 XHTMLRequest 的 GET/POST/PUT/DELETE 服务器响应也必须有以下头部:

access-control-allow-origin: origin

“ source”在 请求头中(浏览器会将其添加到请求中)。例如:

Origin: http://localhost:4221

你可以像下面这样添加响应头来接受所有:

access-control-allow-origin: *

或特定请求的响应头,如:

access-control-allow-origin: http://localhost:4221

浏览器中的消息不清楚,无法理解: “ ... 请求的资源”

请注意: CORS 适用于本地主机。不同的端口意味着不同的域。 如果收到错误消息,请检查服务器端的 CORS 配置。

在大多数住房服务中,只需在目标服务器文件夹的. htaccess 中添加以下内容:

标题设置访问-控制-允许-起源’https://your.site.folder’

我也有同样的问题。在我的情况下,我修复了它通过添加参数的 timestamp到我的网址。即使这是不需要的服务器,我正在访问。

例如 yoururl.com/yourdocument?timestamp=1234567

注意: 我使用了 pos 时间戳