XMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头 [英] XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header

查看:53
本文介绍了XMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

tl;博士;关于同源政策

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

<块引用>

XMLHttpRequest 无法加载 标准,该标准允许 Bob 明确授予 Mallory 站点通过 Alice 的浏览器访问数据的权限.

一个基本的实现只包括:

Access-Control-Allow-Origin:*

... 在响应头中允许任何网站读取数据.

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

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

Bob 如何设置响应标头的细节取决于 Bob 的 HTTP 服务器和/或服务器端编程语言.

注意:有些请求很复杂,会发送预检a> OPTIONS 请求,在浏览器发送 JS 想要发出的 GET/POST/PUT/Whatever 请求之前,服务器必须响应该请求.仅将 Access-Control-Allow-Origin 添加到特定 URL 的 CORS 实现通常会因此而失败.


显然,通过 CORS 授予权限是 Bob 仅在以下情况下才会执行的操作:

  • 数据不是私人的
  • 马洛里值得信赖

但我不是鲍勃!

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

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


提及预检响应"的错误消息

一些跨源请求是预检.

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

  • 包括 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-Typecode> 和 Accept).

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


不透明的响应

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

如果您使用 fetch API(而不是XMLHttpRequest),然后你可以配置它不尝试使用CORS.

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

它会让您发出一个简单的请求,而不是看到响应,并且不会在开发者控制台中填充错误消息.

当您使用 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 之类的 hack 来提供数据,这就是人们的方式在 CORS 出现之前做了跨源 Ajax.

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

它要求 Mallory 相信 Bob 不会提供恶意代码.

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

由于 JSONP 通过附加 <script> 元素以 JavaScript 程序的形式加载数据,该程序调用页面中已有的函数,尝试在 URL 上使用 JSONP 技术返回 JSON 会失败——通常会出现 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 标头.

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

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

拥有合适的开发环境和本地开发服务器通常是更好的方法.


其他安全风险

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


总结

  • 您无法在您的客户端代码中执行任何操作来启用 CORS 访问其他服务器的权限.
  • 如果您控制服务器,则向其发出请求:向其添加 CORS 权限.
  • 如果您与控制它的人友好:让他们为其添加 CORS 权限.
  • 如果是公共服务:
    • 阅读他们的 API 文档,了解他们对使用客户端 JavaScript 访问它的看法:
      • 他们可能会告诉您使用特定的网址
      • 他们可能支持 JSONP
      • 它们可能根本不支持从客户端代码进行跨域访问(这可能是出于安全考虑的深思熟虑的决定,尤其是当您必须在每个请求中传递个性化 API 密钥时).
    • 确保您没有触发不需要的预检请求.API 可能会授予简单请求的权限,但不会授予预检请求的权限.
  • 如果以上都不适用:让浏览器改为与您的服务器通信,然后让您的服务器从其他服务器获取数据并将其传递.(还有第三方托管服务将 CORS 标头附加到您可以使用的可公开访问的资源).

tl;dr; About the Same Origin Policy

I have a Grunt process which initiates an instance of express.js server. This was working absolutely fine up until just now when it started serving a blank page with the following appearing in the error log in the developer's console in Chrome (latest version):

XMLHttpRequest cannot load https://www.example.com/ No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4300' is therefore not allowed access.

What is stopping me from accessing the page?

解决方案

tl;dr — There's a summary at the end and headings in the answer to make it easier to find the relevant parts. Reading everything is recommended though as it provides useful background for understanding the why that makes seeing how the how applies in different circumstances easier.

About the Same Origin Policy

This is the Same Origin Policy. It is a security feature implemented by browsers.

Your particular case is showing how it is implemented for XMLHttpRequest (and you'll get identical results if you were to use fetch), but it also applies to other things (such as images loaded onto a <canvas> or documents loaded into an <iframe>), just with slightly different implementations.

(Weirdly, it also applies to CSS fonts, but that is because found foundries insisted on DRM and not for the security issues that the Same Origin Policy usually covers).

The standard scenario that demonstrates the need for the SOP can be demonstrated with three characters:

  • Alice is a person with a web browser
  • Bob runs a website (https://www.[website].com/ in your example)
  • Mallory runs a website (http://localhost:4300 in your example)

Alice is logged into Bob's site and has some confidential data there. Perhaps it is a company intranet (accessible only to browsers on the LAN), or her online banking (accessible only with a cookie you get after entering a username and password).

Alice visits Mallory's website which has some JavaScript that causes Alice's browser to make an HTTP request to Bob's website (from her IP address with her cookies, etc). This could be as simple as using XMLHttpRequest and reading the responseText.

The browser's Same Origin Policy prevents that JavaScript from reading the data returned by Bob's website (which Bob and Alice don't want Mallory to access). (Note that you can, for example, display an image using an <img> element across origins because the content of the image is not exposed to JavaScript (or Mallory) … unless you throw canvas into the mix in which case you will generate a same-origin violation error).


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

For any given URL it is possible that the SOP is not needed. A couple of common scenarios where this is the case are:

  • Alice, Bob and Mallory are the same person.
  • Bob is providing entirely public information

… but the browser has no way of knowing if either of the above are true, so trust is not automatic and the SOP is applied. Permission has to be granted explicitly before the browser will give the data it was given to a different website.


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

Browser extensions*, the Network tab in browser developer tools and applications like Postman are installed software. They aren't passing data from one website to the JavaScript belonging to a different website just because you visited that different website. Installing software usually takes a more conscious choice.

There isn't a third party (Mallory) who is considered a risk.

* Browser extensions do need to be written carefully to avoid cross-origin issues. See the Chrome documentation for example.


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

There are a number of circumstances where Mallory's site can cause a browser to fetch data from a third party and display it (e.g. by adding an <img> element to display an image). It isn't possible for Mallory's JavaScript to read the data in that resource though, only Alice's browser and Bob's server can do that, so it is still secure.


CORS

The Access-Control-Allow-Origin HTTP response header referred to in the error message is part of the CORS standard which allows Bob to explicitly grant permission to Mallory's site to access the data via Alice's browser.

A basic implementation would just include:

Access-Control-Allow-Origin: *

… in the response headers to permit any website to read the data.

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

… would allow only a specific site to access it, and Bob can dynamically generate that based on the Origin request header to permit multiple, but not all, sites to access it.

The specifics of how Bob sets that response header depend on Bob's HTTP server and/or server-side programming language. There is a collection of guides for various common configurations that might help.

NB: Some requests are complex and send a preflight OPTIONS request that the server will have to respond to before the browser will send the GET/POST/PUT/Whatever request that the JS wants to make. Implementations of CORS that only add Access-Control-Allow-Origin to specific URLs often get tripped up by this.


Obviously granting permission via CORS is something Bob would only do only if either:

  • The data was not private or
  • Mallory was trusted

But I'm not Bob!

There is no standard mechanism for Mallory to add this header because it has to come from Bob's website, which she does not control.

If Bob is running a public API then there might be a mechanism to turn on CORS (perhaps by formatting the request in a certain way, or a config option after logging into a Developer Portal site for Bob's site). This will have to be a mechanism implemented by Bob though. Mallory could read the documentation on Bob's site to see if something is available, or she could talk to Bob and ask him to implement CORS.


Error messages which mention "Response for preflight"

Some cross origin requests are preflighted.

This happens when (roughly speaking) you try to make a cross-origin request that:

  • Includes credentials like cookies
  • Couldn't be generated with a regular HTML form (e.g. has custom headers or a Content-Type that you couldn't use in a form's enctype).

If you are correctly doing something that needs a preflight

In these cases then the rest of this answer still applies but you also need to make sure that the server can listen for the preflight request (which will be OPTIONS (and not GET, POST or whatever you were trying to send) and respond to it with the right Access-Control-Allow-Origin header but also Access-Control-Allow-Methods and Access-Control-Allow-Headers to allow your specific HTTP methods or headers.

If you are triggering a preflight by mistake

Sometimes people make mistakes when trying to construct Ajax requests, and sometimes these trigger the need for a preflight. If the API is designed to allow cross-origin requests, but doesn't require anything that would need a preflight, then this can break access.

Common mistakes that trigger this include:

  • trying to put Access-Control-Allow-Origin and other CORS response headers on the request. These don't belong on the request, don't do anything helpful (what would be the point of a permissions system where you could grant yourself permission?), and must appear only on the response.
  • trying to put a Content-Type: application/json header on a GET request that has no request body to describe the content of (typically when the author confuses Content-Type and Accept).

In either of these cases, removing the extra request header will often be enough to avoid the need for a preflight (which will solve the problem when communicating with APIs that support simple requests but not preflighted requests).


Opaque responses

Sometimes you need to make an HTTP request, but you don't need to read the response. e.g. if you are posting a log message to the server for recording.

If you are using the fetch API (rather than XMLHttpRequest), then you can configure it to not try to use CORS.

Note that this won't let you do anything that you require CORS to do. You will not be able to read the response. You will not be able to make a request that requires a preflight.

It will let you make a simple request, not see the response, and not fill the Developer Console with error messages.

How to do it is explained by the Chrome error message given when you make a request using fetch and don't get permission to view the response with CORS:

Access to fetch at 'https://example.com/' from origin 'https://example.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Thus:

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


Alternatives to CORS

JSONP

Bob could also provide the data using a hack like JSONP which is how people did cross-origin Ajax before CORS came along.

It works by presenting the data in the form of a JavaScript program which injects the data into Mallory's page.

It requires that Mallory trust Bob not to provide malicious code.

Note the common theme: The site providing the data has to tell the browser that it is OK for a third party site to access the data it is sending to the browser.

Since JSONP works by appending a <script> element to load the data in the form of a JavaScript program which calls a function already in the page, attempting to use the JSONP technique on a URL which returns JSON will fail — typically with a CORB error — because JSON is not JavaScript.

Move the two resources to a single Origin

If the HTML document the JS runs in and the URL being requested are on the same origin (sharing the same scheme, hostname, and port) then they Same Origin Policy grants permission by default. CORS is not needed.

A Proxy

Mallory could use server-side code to fetch the data (which she could then pass from her server to Alice's browser through HTTP as usual).

It will either:

  • add CORS headers
  • convert the response to JSONP
  • exist on the same origin as the HTML document

That server-side code could be written & hosted by a third party (such as CORS Anywhere). Note the privacy implications of this: The third party can monitor who proxies what across their servers.

Bob wouldn't need to grant any permissions for that to happen.

There are no security implications here since that is just between Mallory and Bob. There is no way for Bob to think that Mallory is Alice and to provide Mallory with data that should be kept confidential between Alice and Bob.

Consequently, Mallory can only use this technique to read public data.

Do note, however, that taking content from someone else's website and displaying it on your own might be a violation of copyright and open you up to legal action.

Writing something other than a web app

As noted in the section "Why the Same Origin Policy only applies to JavaScript in a web page", you can avoid the SOP by not writing JavaScript in a webpage.

That doesn't mean you can't continue to use JavaScript and HTML, but you could distribute it using some other mechanism, such as Node-WebKit or PhoneGap.

Browser extensions

It is possible for a browser extension to inject the CORS headers in the response before the Same Origin Policy is applied.

These can be useful for development, but are not practical for a production site (asking every user of your site to install a browser extension that disables a security feature of their browser is unreasonable).

They also tend to work only with simple requests (failing when handling preflight OPTIONS requests).

Having a proper development environment with a local development server is usually a better approach.


Other security risks

Note that SOP / CORS do not mitigate XSS, CSRF, or SQL Injection attacks which need to be handled independently.


Summary

  • There is nothing you can do in your client-side code that will enable CORS access to someone else's server.
  • If you control the server the request is being made to: Add CORS permissions to it.
  • If you are friendly with the person who controls it: Get them to add CORS permissions to it.
  • If it is a public service:
    • Read their API documentation to see what they say about accessing it with client-side JavaScript:
      • They might tell you to use specific URLs
      • They might support JSONP
      • They might not support cross-origin access from client-side code at all (this might be a deliberate decision on security grounds, especially if you have to pass a personalised API Key in each request).
    • Make sure you aren't triggering a preflight request you don't need. The API might grant permission for simple requests but not preflighted requests.
  • If none of the above apply: Get the browser to talk to your server instead, and then have your server fetch the data from the other server and pass it on. (There are also third-party hosted services which attach CORS headers to publically accessible resources that you could use).

这篇关于XMLHttpRequest 无法加载 XXX No 'Access-Control-Allow-Origin' 标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆