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

问题描述 投票:79回答:6

TL;博士;关于同源政策

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

XMLHttpRequest无法加载https://www.example.com/请求的资源上没有“Access-Control-Allow-Origin”标头。因此,'http://localhost:4300'原产地不允许进入。

什么阻止我访问该页面?

javascript cors same-origin-policy
6个回答
128
投票

About the Same Origin Policy

这是Same Origin Policy。它是由浏览器实现的安全功能。

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

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

可以使用three characters演示证明SOP需求的标准方案:

  • Alice是一个拥有Web浏览器的人
  • 鲍勃经营一个网站(在你的例子中https://www.[website].com/
  • Mallory运行一个网站(在你的例子中http://localhost:4300

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

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

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


Why the Same Origin Policy applies when you don't think it should

对于任何给定的URL,可能不需要SOP。在这种情况下,有两种常见情况是:

  • Alice,Bob和Mallory是同一个人。
  • 鲍勃提供完全公开的信息

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


Why the Same Origin Policy only applies to JavaScript in a web page

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

没有第三方(Mallory)被视为风险。


Why you can display data in the page without reading it with JS

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


CORS

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

基本实现只包括:

Access-Control-Allow-Origin: *

......允许任何网站阅读数据。

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

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

设置响应头的具体细节取决于Bob的HTTP服务器和/或服务器端编程语言。有a collection of guides for various common configurations可能会有所帮助。

Model of where CORS rules are applied

注意:有些请求很复杂,并发送一个preflight OPTIONS请求,服务器必须在浏览器发送GET / POST / PUT /无论JS想要的任何请求之前做出响应。仅将Access-Control-Allow-Origin添加到特定URL的CORS实现通常会因此而被绊倒。


显然,通过CORS授予权限是Bob只有在以下任何一种情况下才会执行的操作:

  • 数据不是私人或
  • 马洛里很受信任

如果您在这种情况下也是Bob,那么您添加CORS权限标头的具体细节将取决于您选择的HTTP服务器软件的某些组合以及您用于服务器端编程的语言(如果有)。

Mallory无法添加此标题,因为她必须获得Bob的网站的许可,并且为了能够授予自己许可,这将是愚蠢的(使SOP变得毫无用处)。


Error messages which mention "Response for preflight"

一些交叉来源请求是preflighted

这种情况发生在(大致说来)您尝试制作跨源请求时:

  • 包括cookie等凭据
  • 无法使用常规HTML表单生成(例如,具有自定义标题或您在表单的enctype中无法使用的内容类型)。

如果你正确地做了一些需要预检的事情

在这些情况下,这个答案的其余部分仍然适用,但你还需要确保服务器可以监听预检请求(这将是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时)。

在上述任何一种情况下,删除额外的请求标头通常足以避免需要预检。


Opaque responses

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

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

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

它将允许您发出一个简单的请求,而不是查看响应,并且不会向Developer Console添加错误消息。

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

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

从而:

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

Alternatives to CORS

JSONP

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

它的工作原理是以JavaScript程序的形式呈现数据,该程序将数据注入Mallory的页面。

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

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

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

将两个资源移动到单个Origin

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

代理人

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

它将:

  • 添加CORS标头
  • 将响应转换为JSONP
  • 存在于与HTML文档相同的原点

服务器端代码可以由第三方(例如YQL)托管。

Bob不需要为此发布任何权限。

这很好,因为那只是马洛里和鲍勃之间。 Bob没有办法认为Mallory是Alice并且为Mallory提供了应该在Alice和Bob之间保密的数据。

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

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

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

这并不意味着您无法继续使用JavaScript和HTML,但您可以使用其他一些机制(例如Node-WebKit或PhoneGap)进行分发。

Browser extensions

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

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

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

拥有适当的开发环境和本地开发服务器通常是一种更好的方法。


Other security risks

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


Summary

  • 您无法在客户端代码中执行任何操作来启用CORS访问其他人的服务器。
  • 如果您控制服务器,则请求:向其添加CORS权限。
  • 如果您对控制它的人很友好:让他们为其添加CORS权限。
  • 如果是公共服务:请阅读他们的API文档,了解他们对使用客户端JavaScript访问它的看法。他们可能会告诉您使用特定的URL或使用JSONP(或者他们可能根本不支持它)。
  • 如果以上都不适用:让浏览器改为与您的服务器通信,然后让您的服务器从其他服务器获取数据并将其传递。 (还有第三方托管服务将CORS标头附加到您可以使用的公共可访问资源)。

3
投票

目标服务器必须允许跨源请求。为了让它通过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);
});

2
投票

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

在这里阅读更多相关信息:CORS error with jquery

要解决此问题,如果您有权访问其他域,则必须在服务器中允许Access-Control-Allow-Origin。这可以添加到标题中。您可以为所有请求/域或特定域启用此功能。

How to get a cross-origin resource sharing (CORS) post request working

这些链接可能有帮助


2
投票

因为在接受的答案中没有提到这一点。

  • 对于这个确切的问题,情况并非如此,但可能有助于其他人搜索该问题
  • 这是您可以在客户端代码中执行的操作,以防止在某些情况下出现CORS错误。

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

如果您的客户端代码未在请求中显式设置受影响的标头(例如“Accept”)并且可能会发生某些客户端使用某些“非标准”值自动设置这些标头,导致服务器不接受它简单请求 - 这会给你一个CORS错误。


-1
投票

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

我目前有这个问题有不同的原因。我的前端也返回'Access-Control-Allow-Origin'标题错误。

只是我已经指出错误的URL,所以这个标题没有正确反映(我保持假设它没有)。 localhost(前端) - >调用非安全http(应该是https),确保前端的API端点指向正确的协议。


-4
投票

您应该启用CORS以使其正常工作。

© www.soinside.com 2019 - 2024. All rights reserved.