Access-Control-Allow-Origin标头如何工作?


1152

显然,我完全误解了它的语义。我想到了这样的事情:

  1. 从客户端下载JavaScript代码MyCode.js http://siteA- 起源
  2. MyCode.js的响应标头包含Access-Control-Allow-Originhttp://siteB:,我认为这意味着MyCode.js被允许对站点B进行跨域引用。
  3. 客户端会触发MyCode.js的某些功能,这些功能继而向发出请求http://siteB,尽管是跨域请求,但也应该很好。

好吧,我错了。它根本不像这样工作。因此,我阅读了跨域资源共享,并尝试阅读w3c建议中的跨域资源共享

可以确定的一件事-我仍然不明白我应该如何使用此标头。

我对站点A和站点B都拥有完全控制权。如何使用此标头使从站点A下载的javascript代码能够访问站点B上的资源?

聚苯乙烯

我不想利用JSONP。


3
我不确定,但是我相信以这种方式设置标头允许站点B上的代码访存http://siteA/MyCode.js
pimvdb

6
但是如何?为了获得标头值,必须首先获取资源,但是资源是跨源的,因此浏览器是否不应该首先阻止请求?
2012年

您所描述的实际上类似于另一种做法,即内容安全策略
Alex

3
@mark您不必获取资源即可获取标头。HTTP HEADER方法将仅返回标头。对于CORS,使用HTTP OPTIONS方法进行预检检查,该方法也不返回主体。apsillers的答案很好地描述了这个stackoverflow.com/posts/10636765/revisions
马修

Answers:


1444

Access-Control-Allow-OriginCORS(跨源资源共享)标头

当站点A尝试从站点B获取内容时,站点B可以发送Access-Control-Allow-Origin响应标头以告知浏览器某些原始来源可以访问此页面的内容。(来源域,再加上方案和端口号。)默认情况下,其他任何来源无法访问站点B的页面。使用Access-Control-Allow-Origin标头会打开一扇门,可以按特定的请求来源进行跨域访问。

对于站点B希望站点A可以访问的每个资源/页面,站点B应该为其页面提供响应头:

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

现代浏览器不会完全阻止跨域请求。如果站点A从站点B请求一个页面,则浏览器实际上将在网络级别上获取请求的页面并检查响应头是否将站点A列为允许的请求者域。如果网站B未表明允许网站A访问此页面,则浏览器将触发XMLHttpRequesterror事件,并拒绝对请求JavaScript代码的响应数据。

非简单请求

什么发生在网络层面可以略微比上述解释更加复杂。如果该请求是“非简单”请求,则浏览器将首先发送一个无数据的“预检” OPTIONS请求,以验证服务器将接受该请求。当一个(或两个)同时发生时,请求不是简单的:

  • 使用GET或POST以外的HTTP动词(例如PUT,DELETE)
  • 使用非简单的请求标头;仅有的简单请求标头是:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(当它的值是这仅仅是简单的application/x-www-form-urlencodedmultipart/form-datatext/plain

如果服务器使用与非简单动词和/或非简单头匹配的适当响应头(Access-Control-Allow-Headers对于非简单头,Access-Control-Allow-Methods非简单动词)响应OPTIONS预检,则浏览器将发送实际请求。

假设站点A要发送的PUT请求/somePage,其非简单Content-Type值为application/json,则浏览器将首先发送预检请求:

OPTIONS /somePage HTTP/1.1
Origin: http://siteA.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

请注意,Access-Control-Request-MethodAccess-Control-Request-Headers是由浏览器自动添加的;您无需添加它们。此OPTIONS预检会获取成功的响应标头:

Access-Control-Allow-Origin: http://siteA.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type

发送实际请求时(完成预检后),其行为与处理简单请求的方式相同。换句话说,将预检成功的非简单请求与简单请求视为相同(即,服务器仍必须Access-Control-Allow-Origin再次发送实际响应)。

浏览器发送实际请求:

PUT /somePage HTTP/1.1
Origin: http://siteA.com
Content-Type: application/json

{ "myRequestContent": "JSON is so great" }

然后服务器发送回Access-Control-Allow-Origin,就像一个简单的请求一样:

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

有关非简单请求的更多信息,请参阅通过CORS了解XMLHttpRequest


4
但是,首先,MyCode.js无法访问网站B!此标头如何到达客户端?顺便说一句,化身中轻型滑翔机的荣誉。
标记

8
我进行了明确的编辑:浏览器实际上确实在站点B上执行网络获取以检查Access-Control-Allow-Origin标头,但是如果标头不允许站点A拥有它,它可能不会提供对站点A上JS代码的响应。(PS谢谢:))
ps弹手2012年

2
实际上,除非跨域请求得到批准,否则我在Fiddler中看不到任何下载记录。有趣的…
2012年

23
@ Jwan622一个基本的“ 为什么? ”这样的问题可能超出了此特定答案的范围,该问题仅涉及规则和机制。基本上,浏览器使,坐在计算机上的人都可以看到来自任何来源的任何资源。它不允许脚本(可由任何人编写)从与运行脚本的页面的来源不同的来源读取资源。一些相关的问题是programmers.stackexchange.com/q/216605,并且相同来源策略的威胁模型是什么?
apsillers

3
如果使用身份验证,Access-Control-Allow-Origin*在某些浏览器(FF和Chrome AFAIK)中不接受。因此,在这种情况下,您必须从Origin标题中指定值。希望这会帮助某人。
Zsolti

123

跨域资源共享- CORS(AKA跨域AJAX请求)是大多数Web开发人员可能遇到的问题,根据Same-Origin-Policy,浏览器将客户端JavaScript限制在安全沙箱中,通常JS无法直接与远程服务器通信来自其他域。过去,开发人员创建了许多棘手的方法来实现跨域资源请求,最常用的方法是:

  1. 使用Flash / Silverlight或服务器端作为“代理”与远程通信。
  2. 带有填充的JSON(JSONP)。
  3. 将远程服务器嵌入到iframe中,并通过fragment或window.name进行通信,请参见此处

这些棘手的方式或多或少都存在一些问题,例如,如果开发人员只是简单地“评估”,JSONP可能会导致安全漏洞;而上面的#3,虽然可行,但两个域之间应该建立严格的契约,既不灵活也不优雅恕我直言:)

W3C引入了跨域资源共享(CORS)作为标准解决方案,以提供安全,灵活和推荐的标准方式来解决此问题。

机制

从较高的层次上,我们可以简单地认为CORS是域A的客户端AJAX调用与域B上托管的页面之间的合同,典型的跨域请求/响应为:

DomainA AJAX请求标头

Host DomainB.com
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language en-us;
Accept-Encoding gzip, deflate
Keep-Alive 115
Origin http://DomainA.com 

DomainB响应标头

Cache-Control private
Content-Type application/json; charset=utf-8
Access-Control-Allow-Origin DomainA.com
Content-Length 87
Proxy-Connection Keep-Alive
Connection Keep-Alive

我在上面标记的蓝色部分是核心事实,“ Origin”请求标头“指示跨域请求或预检请求的来源”,“ Access-Control-Allow-Origin”响应标头指示此页面允许来自远程请求DomainA(如果值为*,则表示允许来自任何域的远程请求)。

正如我上面提到的,W3建议浏览器在提交实际的跨域HTTP请求之前实现一个“ 预检请求 ”,简而言之,它是一个HTTP OPTIONS请求:

OPTIONS DomainB.com/foo.aspx HTTP/1.1

如果foo.aspx支持OPTIONS HTTP动词,则它可能返回如下响应:

HTTP/1.1 200 OK
Date: Wed, 01 Mar 2011 15:38:19 GMT
Access-Control-Allow-Origin: http://DomainA.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Max-Age: 1728000
Connection: Keep-Alive
Content-Type: application/json

仅当响应包含“ Access-Control-Allow-Origin”并且其值为“ *”或包含提交CORS请求的域时,通过满足此强制性条件,浏览器将提交实际的跨域请求,并缓存结果在“ 预检结果缓存 ”中。

我三年前写了关于CORS的博客:AJAX跨源HTTP请求


这个答案使我意识到了为什么我突然在不使用此标头进行POST和GET请求的情况下遇到问题。我不小心直接从磁盘打开了index.html文件,因此客户端在node.js上访问的URL被认为是跨域的,而它只是在本地主机上运行。通过URL访问(通常会这样做)“解决了”我的问题……
LuqJensen

外部网络中的域是否可以与内部网络中的域通信?
2013年

68

问题有点老,无法回答,但是我将其发布,以供将来对该问题的任何参考。

根据 Mozilla开发人员网络文章,

当资源从与第一个资源本身所服务的域或端口不同的域或端口请求资源时,它会发出跨域HTTP请求

在此处输入图片说明

从投放的HTML页面http://domain-a.com对发出<img>src请求http://domain-b.com/image.jpg
如今,网络上的许多页面从不同的域加载资源,例如CSS样式表图像脚本(因此应该很酷)。

原产地政策

出于安全原因,浏览器限制跨域HTTP了从脚本内部发起的请求。
例如,XMLHttpRequestFetch遵循同源策略
因此,Web应用程序使用XMLHttpRequestFetch只能使HTTP请求自己的域

跨域资源共享(CORS)

为了改善Web应用程序,开发人员要求浏览器供应商允许跨域请求。

跨来源资源共享(CORS)机制给出web服务器跨域的访问控制,这使安全跨域数据传送。
现代浏览器在API容器中使用CORS(例如或)来减轻跨源HTTP请求的风险。XMLHttpRequestFetch

CORS的工作方式(Access-Control-Allow-Origin标头)

维基百科

CORS标准描述了新的HTTP标头,这些标头为浏览器和服务器提供了一种仅在获得许可时才请求远程URL的方法。

尽管服务器可以执行某些验证和授权,但是浏览器通常有责任支持这些标头并遵守其施加的限制。

  1. 浏览器发送OPTIONS带有Origin HTTP标头的请求。

    此标头的值是为父页面提供服务的域。当页面来自http://www.example.com尝试访问中的用户数据时service.example.com,以下请求标头将发​​送到service.example.com

    起源: http //www.example.com

  2. 服务器位于 service.example.com可能会回应:

    • 一个Access-Control-Allow-Origin在其响应中指示哪些原点位点(ACAO)报头是允许的。
      例如:

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

    • 如果服务器不允许跨域请求,则显示错误页面

    • 一个Access-Control-Allow-Origin具有一个通配符的是(ACAO)报头允许所有的结构域:

      Access-Control-Allow-Origin: *


1
如何设置任何对象都不可让某些东西Access-Control-Allow-Origin:null
冒出来

2
当我不想允许任何人通过CORS访问我的资源时,我应该设置什么值Access-Control-Allow-Origin?我的意思是Access-Control-Allow-Origin: *
苏宾·查利尔(Subin Chalil)

4
为此,请不要设置任何内容
Pmpr

24

每当我开始考虑CORS时,我直觉就知道哪个网站托管标头,就像您在问题中所描述的那样。对我来说,考虑同一个原产地政策的目的很有帮助。

相同来源策略的目的是保护您免受siteA.com上恶意Java脚本的访问,以访问您选择仅与siteB.com共享的私人信息。如果没有相同的原始策略,由siteA.com的作者编写的JavaScript可能会使用您对siteB.com的身份验证cookie,使您的浏览器向siteB.com发出请求。这样,siteA.com可以窃取您与siteB.com共享的秘密信息。

有时您需要跨域工作,而这正是CORS的源头。CORS放宽了domainB.com的原始策略,使用Access-Control-Allow-Origin标头列出了可信任的其他域(domainA.com),该域可运行可与domainA进行交互的JavaScript。 com。

要了解哪个域应服务CORS标头,请考虑这一点。您访问的恶意网站.com包含一些JavaScript,这些JavaScript试图向mybank.com发出跨域请求。决定是否设置CORS标头以放宽相同的源策略,以允许来自恶意网站的JavaScript与之交互,应该由mybank.com(而不是恶意网站)来决定。如果malicous.com可以设置其自己的CORS标头,以允许其自己的JavaScript访问mybank.com,则这将完全废除该原始策略。

我认为直觉不佳的原因是开发网站时的观点。这是我的网站,使用了所有的JavaScript,因此它没有做任何恶意的事情,应该由来指定我的 JavaScript可以与哪些其他网站进行交互。实际上,我何时应该在考虑JavaScript尝试与其他网站进行哪些交互,我是否应该使用CORS允许它们进行交互?


1
给定第2段,您在第3段中是否有siteA和siteB?我可能会误会,但是前面的段落似乎暗示它的siteA正在运行有问题的JS?
cellepo

11

1.客户端从源地址http:// siteA下载javascript代码MyCode.js 。

进行下载的代码-您的html脚本标记或来自javascript的xhr或其他内容-假设来自http:// siteZ。并且,当浏览器请求MyCode.js时,它将发送一个Origin:标头,标明 “ Origin:http:// siteZ ”,因为它可以看到您正在请求siteA和siteZ!= siteA。(您不能停止或干预。)

2. MyCode.js的响应标头包含Access-Control-Allow-Origin:http:// siteB,我认为这意味着MyCode.js被允许对站点B进行跨域引用。

没有。这意味着,仅siteB被允许执行此请求。因此,从siteZ对MyCode.js的请求反而会出错,并且浏览器通常什么也没有给您。但是,如果使服务器返回ACAO:siteZ,则会得到MyCode.js。或者,如果它发送“ *”,那么它将起作用,它将允许所有人进入。或者,如果服务器始终从Origin:标头发送字符串,但是...出于安全考虑,如果您担心黑客的话,您的服务器应只允许允许进入这些请求的候选清单中的来源。

然后,MyCode.js来自siteA。当它向siteB发出请求时,它们都是跨域的,浏览器将发送Origin:siteA,并且siteB必须使用siteA,将其识别为允许的简短请求者列表,然后发送回ACAO:siteA。只有这样,浏览器才会让您的脚本获取这些请求的结果。


11

使用ReactAxios,将代理链接连接到URL并添加标题,如下所示

https://cors-anywhere.herokuapp.com/ + Your API URL

只需添加Proxy链接即可,但也会再次为“无法访问”引发错误。因此最好添加标题,如下所示。

axios.get(`https://cors-anywhere.herokuapp.com/[YOUR_API_URL]`,{headers: {'Access-Control-Allow-Origin': '*'}})
      .then(response => console.log(response:data);
  }

18
请不要这样做。使用代理链接就像将用户cookie交给中间人一样。应该是非法的恕我直言
anthonymonori

这对我很有用!除了使用*(存在安全问题)以外,我将访问控制限制为我要用来学习的确切地址... reqres.in/api/register
C-Note187

9

如果您只想测试跨浏览器阻止您的请求的跨域应用程序,则可以在不安全的模式下打开浏览器并测试您的应用程序,而无需更改代码,也不会使代码不安全。在MAC OS中,您可以从终端行执行此操作:

open -a Google\ Chrome --args --disable-web-security --user-data-dir

9

如果您使用的是PHP,请尝试在php文件的开头添加以下代码:

如果使用本地主机,请尝试以下操作:

header("Access-Control-Allow-Origin: *");

如果使用服务器等外部域,请尝试以下操作:

header("Access-Control-Allow-Origin: http://www.website.com");

7

我使用快递4和节点7.4和angular,我遇到了相同的问题,我需要帮助:
a)服务器端:在app.js文件中,我给所有响应的标头如下:

app.use(function(req, res, next) {  
      res.header('Access-Control-Allow-Origin', req.headers.origin);
      res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      next();
 });  

这必须在所有路由器之前
我看到了很多添加的标题:

res.header("Access-Control-Allow-Headers","*");
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');

但我不需要那个,
b)客户端:在发送ajax中,您需要添加:“ withCredentials:true”,例如:

$http({
     method: 'POST',
     url: 'url, 
     withCredentials: true,
     data : {}
   }).then(function(response){
        // code  
   }, function (response) {
         // code 
   });

祝好运。


res.header('Access-Control-Allow-Origin', req.headers.origin);res.header('Access-Control-Allow-Origin', '*');
Aelfinn

4

对于跨源共享,请设置标头: 'Access-Control-Allow-Origin':'*';

Php: header('Access-Control-Allow-Origin':'*');

节点: app.use('Access-Control-Allow-Origin':'*');

这将允许共享不同域的内容。


4

在Python中,我一直在使用该Flask-CORS成功。它使与CORS的交往变得超级轻松而轻松。我从下面的库文档中添加了一些代码。

安装:

$ pip install -U flask-cors

一个简单的示例,它允许所有路由上的所有域都使用CORS:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"

有关更具体的示例,请参阅文档。我已经使用上面的简单示例解决了正在构建的离子应用程序中的CORS问题,该应用程序必须访问单独的烧瓶服务器。


4

根据我自己的经验,很难找到一个简单的解释,为什么CORS甚至是一个问题。

一旦了解了它的原因,标题和讨论就会变得更加清晰。我将在几行中给它一个镜头。


这都是关于cookie的。Cookies按其域存储在客户端上。

一个示例故事:在您的计算机上,有个Cookie yourbank.com。也许你的会议在那里。

关键点:当客户端向服务器发出请求时,它将发送存储在客户端所在域下的cookie。

您已在浏览器中登录yourbank.com。您要求查看所有帐户。 yourbank.com收到一堆Cookie并发回其响应(您的帐户)。

如果另一个客户端向服务器发出跨源请求,则这些cookie会像以前一样被发送。ro

您浏览到malicious.com。恶意向包括在内的不同银行发出了一堆请求yourbank.com

由于cookie已按预期进行验证,因此服务器将授权响应。

这些Cookie被收集起来并一起发送-现在,malicious.com来自的回复yourbank

kes。


现在,一些问题和答案变得显而易见了:

  • “为什么我们不只是阻止浏览器这样做呢?” 是的 CORS。
  • “我们如何解决它?” 让服务器告知请求CORS可以。

3

只需将以下代码粘贴到您的web.config文件中。

注意,您必须将以下代码粘贴到<system.webServer>标签下

    <httpProtocol>  
    <customHeaders>  
     <add name="Access-Control-Allow-Origin" value="*" />  
     <add name="Access-Control-Allow-Headers" value="Content-Type" />  
     <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />  
    </customHeaders>  
  </httpProtocol>  

0

Access-Control-Allow-Origin响应标头指示是否可以与来自给定源的请求代码共享响应。

Header type Response       header
Forbidden header name      no

指示浏览器允许任何来源的代码访问资源的响应将包括以下内容:

Access-Control-Allow-Origin: *

欲了解更多信息,请访问这里 ....


0

Nginx和Appache

除了自动起飞回答外,我想添加Wiki图形,该图形显示请求何时简单(以及是否发送OPTIONS飞行前请求)

在此处输入图片说明

对于简单的请求(例如,热链接图像),您无需更改服务器配置文件,但可以在应用程序中添加标头(托管在服务器中,例如在php中),例如Melvin Guerrero在他的回答中提到的-但请记住:如果添加完整服务器(配置)中的cors标头,并且同时允许在应用程序(例如php)上使用简单的cors,这根本不起作用。

这是两个流行服务器的配置

  • 在Nginx上打开CORSnginx.conf文件)

  • 在Appache上打开CORS.htaccess文件)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.