HTTP访问控制(CORS)
2017-03-25 17:03
387 查看
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
当一个资源请求一个其它域名或者另外一个端口的资源时会产生一个跨域HTTP请求(cross-originHTTPrequest)。比如说,http://domaina.example的某HTML页面通过<img>的src请求http://domainb.foo/image.jpg。在当今的Web开发中,许多页面都会从另外一个站点加载各类资源(包括CSS、图片、JavaScript脚本以及其它类资源)。
出于安全考虑,浏览器会限制脚本中发起的跨域请求。比如,使用Fetch发起HTTP请求就必须遵守同源策略。所以,Web应用程序通过Fetch能且只能向同域名的资源发起HTTP请求,而不能向任何其它域名发起请求。为了改进/提升Web应用程序,开发人员要求浏览器供应商允许跨域请求。(译者注:这段描述跨域不准确,跨域并非浏览器限制了发起跨站请求,而是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是CSRF跨站攻击原理,请求是发送到了后端服务器无论是否跨域!注意:有些浏览器不允许从HTTPS的域跨域访问HTTP,比如Chrome和Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。)
跨源资源共享(CORS)机制让Web应用服务器能支持跨站访问控制,从而使得安全地进行跨站数据传输成为可能。需要特别注意的是,这个规范是针对API容器的(比如说Fetch),以减轻跨域HTTP请求的风险。
这篇文章适用于网站管理员、服务器端程序开发人员以及前端开发人员。现代浏览器支持跨源共享的客户端组件,包括请求头和策略执行。同样,服务器端则需要解析这些新的请求头,并按照策略返回相应的响应头以及所请求的资源。对于服务器端程序开发人员,还可以阅读补充材料cross-originsharingfromaserverperspective(withPHPcodesnippets)。
跨源资源共享标准(cross-originsharingstandard)使得以下场景可以使用跨站HTTP请求:
如上所述,使用Fetch发起跨站HTTP请求。
Web字体(CSS中通过因此,网站就可以发布TrueType字体资源,并只允许已授权网站进行跨站调用。
WebGL贴图
使用
样式表(使用CSSOM)
Scripts(未处理的异常)
接下来的文章,会对跨源资源共享做一个总览,并介绍下需要使用的HTTP头。
概述
跨源资源共享标准通过新增一系列HTTP头,让服务器能声明哪些来源可以通过浏览器访问该服务器上的资源。另外,对那些会对服务器数据造成副作用的HTTP请求方法(特别是Cookies和HTTP认证相关数据)。
随后的章节,将对相关情景及用到的HTTP请求进行讨论。
一些访问控制场景
在此,我们会用三个场景来解释跨源共享是怎么运行的。其中,所有例子使用的都是能发起跨站请求
如果对以下章节中的JavaScript代码片段感兴趣,可以访问http://arunranga.com/examples/access-control/。在所有支持跨站
而如果想继续了解服务器端对跨源请求的处理,则可以访问Server-Side_Access_Control(CORS)。
CORSpreflight。而这部分在本文中被称为“简单请求”,虽然Fetch(定义CORS的)不使用这个术语。满足下述条件的就是“简单请求”:
只允许下列方法:
除了用户代理自动设置的头部外(比如其他任意的Fetch规范定义的“禁止的头部名”),唯一允许人工设置的头部是Fetch规范定义的“CORS-safelistedrequest-header”,如下:
允许的
注意:这些跨站请求与以往浏览器发出的跨站请求并无异同。并且,如果服务器不给出适当的响应头,则不会有任何数据返回给请求方。因此,那些不允许跨站请求的网站无需为这一新的HTTP访问控制特性担心。
注意:WebKitNightly和SafariTechnologyPreview添加了额外的限制在Requirepreflightfornon-standardCORS-safelistedrequestheadersAccept,Accept-Language,andContent-Language,AllowcommasinAccept,Accept-Language,andContent-LanguagerequestheadersforsimpleCORS,andSwitchtoablacklistmodelforrestrictedAcceptheadersinsimpleCORSrequests。其它的浏览器并没有这个限制的实现,因为这个不在规范的内容里。
比如说,假如站点http://foo.example的网页应用想要访问http://bar.other的资源。以下的JavaScript代码应该会在foo.example上执行:
[code;'>varinvocation=newXMLHttpRequest()]varurl='http://bar.other/resources/public-data/';
functioncallOtherDomain(){
if(invocation){
invocation.open('GET',url,true);
invocation.onreadystatechange=handler;
invocation.send();
}
}
这将导致客户端和服务器之间发生简单交换,使用CORS头来处理跨域:
让我们看看,在这个场景中,浏览器会发送什么的请求到服务器,而服务器又会返回什么给浏览器:
[code;'>GET/resources/public-data/HTTP/1.1
Host:bar.other
User-Agent:Mozilla/5.0(Macintosh][code=plain]Origin,它表明了该请求来自于
第13~22行则是http://bar.other服务器的响应。如第16行所示,服务器返回了响应头http://foo.example的跨站请求,它可以返回:
第15至27行是服务器的响应。该响应表明,服务器接受了客服端的跨站请求。具体可以看看第18至21行:
响应头HTTP/1.1Allow:responseheader,但仅限于访问控制的场景下。而响应头Access-Control-Allow-Headers允许以逗号分隔,传递一个可接受的自定义请求头列表。最后,[code]响应头Access-Control-Max-Age告诉浏览器,本次“预请求”的响应结果有效时间是多久。在上面的例子里,1728000秒代表着20天内,浏览器在处理针对该服务器的跨站请求,都可以无需再发送“预请求”,只需根据本次结果进行判断处理。
[code;'>varinvocation=newXMLHttpRequest()]varurl='http://bar.other/resources/credentialed-content/';
functioncallOtherDomain(){
if(invocation){
invocation.open('GET',url,true);
invocation.withCredentials=true;
invocation.onreadystatechange=handler;
invocation.send();
}
如你所见,第七行代码将[code]XMLHttpRequest的
客户端与服务器端交互示例如下:
[code;'>GET/resources/access-control-with-credentials/HTTP/1.1
Host:bar.other
User-Agent:Mozilla/5.0(Macintosh]查看这里.下一部分将讨论实际的HTTP头信息.
HTTP响应头
这部分里列出了跨域资源共享(Cross-OriginResourceSharing)时,服务器端需要返回的响应头信息.上一部分内容是这部分内容在实际运用中的一个概述.
origin参数指定一个允许向该服务器提交请求的URI.对于一个不带有credentials的请求,可以指定为'*',表示允许来自所有域的请求.
举个例子,允许来自http://mozilla.com的请求,你可以这样指定:
可以有多个自定义HTTP请求头,用逗号分隔.
HTTP请求头
这部分内容列出来当浏览器发出跨域请求时可能用到的HTTP请求头.注意这些请求头信息都是在请求服务器的时候已经为你设置好的,当开发者使用跨域的XMLHttpRequest的时候,不需要手动的设置这些头信息.
参数origin是一个URI,告诉服务器端,请求来自哪里.它不包含任何路径信息,只是服务器名.
Note:Origin的值可以是一个空字符串,这是很有用的.
注意,不仅仅是跨域请求,普通请求也会带有ORIGIN头信息.
相关的例子可以在这里找到
相关的例子可以在这里找到
浏览器支持
Desktop
Mobile
相关链接
CodeSamplesShowing
Cross-OrigingResourceSharingFromaServer-SidePerspective(PHP,etc.)
Cross-OriginResourceSharingspecification
FurtherDiscussionoftheOriginHeader
UsingCORSwithAll(Modern)Browsers
UsingCORS-HTML5Rocks
当一个资源请求一个其它域名或者另外一个端口的资源时会产生一个跨域HTTP请求(cross-originHTTPrequest)。比如说,http://domaina.example的某HTML页面通过
出于安全考虑,浏览器会限制脚本中发起的跨域请求。比如,使用
XMLHttpRequest和
XMLHttpRequest对象或
跨源资源共享(
XMLHttpRequest或者
这篇文章适用于网站管理员、服务器端程序开发人员以及前端开发人员。现代浏览器支持跨源共享的客户端组件,包括请求头和策略执行。同样,服务器端则需要解析这些新的请求头,并按照策略返回相应的响应头以及所请求的资源。对于服务器端程序开发人员,还可以阅读补充材料
跨源资源共享标准(
如上所述,使用
XMLHttpRequest或
Web字体(CSS中通过
@font-face使用跨站字体资源),
使用
将Images/video画面绘制到canvas.drawImage
样式表(使用
Scripts(未处理的异常)
接下来的文章,会对跨源资源共享做一个总览,并介绍下需要使用的HTTP头。
概述EDIT
跨源资源共享标准通过新增一系列HTTP头,让服务器能声明哪些来源可以通过浏览器访问该服务器上的资源。另外,对那些会对服务器数据造成副作用的HTTP请求方法(特别是GET以外的HTTP方法,或者搭配某些MIME类型的
POST请求),标准强烈要求浏览器必须先以
OPTIONS请求方式发送一个预请求(preflightrequest),从而获知服务器端对跨源请求所支持HTTP方法。在确认服务器允许该跨源请求的情况下,以实际的HTTP请求方法发送那个真正的请求。服务器端也可以通知客户端,是不是需要随同请求一起发送信用信息(包括
随后的章节,将对相关情景及用到的HTTP请求进行讨论。
一些访问控制场景EDIT
在此,我们会用三个场景来解释跨源共享是怎么运行的。其中,所有例子使用的都是能发起跨站请求XMLHttpRequest对象。
如果对以下章节中的JavaScript代码片段感兴趣,可以访问
XMLHttpRequest请求的浏览中,可以看到实际运行效果。
而如果想继续了解服务器端对跨源请求的处理,则可以访问
简单请求
一些请求不会触发只允许下列方法:
GET
HEAD
POST
除了用户代理自动设置的头部外(比如
Connection,
User-Agent,或者
Accept
Accept-Language
Content-Language
Content-Type(butnotetheadditionalrequirementsbelow)
DPR
Downlink
Save-Data
Viewport-Width
Width
允许的
Content-Type值有:
application/x-www-form-urlencoded
multipart/form-data
text/plain
注意:这些跨站请求与以往浏览器发出的跨站请求并无异同。并且,如果服务器不给出适当的响应头,则不会有任何数据返回给请求方。因此,那些不允许跨站请求的网站无需为这一新的HTTP访问控制特性担心。
注意:WebKitNightly和SafariTechnologyPreview添加了额外的限制在
Accept,
Accept-Language,and
Content-Language头部的值。如果这些头部的值是非标准的,WebKit/Safari就不会将这些请求视为“简单请求”。WebKit/Safari并没有将非标准的头部值列个文档出来,但是可以在WebKitbug里体现:
比如说,假如站点
functioncallOtherDomain(){
if(invocation){
invocation.open('GET',url,true);
invocation.onreadystatechange=handler;
invocation.send();
}
}
这将导致客户端和服务器之间发生简单交换,使用CORS头来处理跨域:
让我们看看,在这个场景中,浏览器会发送什么的请求到服务器,而服务器又会返回什么给浏览器:
Host:bar.other
User-Agent:Mozilla/5.0(Macintosh][code=plain]Origin,它表明了该请求来自于
http://foo.exmaple。
第13~22行则是
Access-Control-Allow-Origin。使用Origin和Access-Control-Allow-Origin就能完成最简单的访问控制。本例中,服务器返回了
Access-Control-Allow-Origin:*,这意味着该资源跨域在跨域行为里可以被任意站点访问。如果服务器端仅允许来自
Access-Control-Allow-Origin:'target='_blank'>http://foo.example[/code]
现在,除了http://foo.example,其它站点就不能跨站访问http://bar.other的资源了(在请求头里ORIGIN中定义,见上述第10行)。 Access-Control-Allow-Origin需要为*或者包含由Origin指明的站点。预请求
不同于上面讨论的简单请求,“预请求”要求必须先发送一个 OPTIONS方法请求给目的站点,来查明这个跨站请求对于目的站点是不是安全的可接受的。这样做,是因为跨站请求可能会对目的站点的数据产生影响。当请求具备以下条件,就会被当成预请求处理:
使用下述方法以外的请求: GET HEAD POST
除了用户代理自动设置的头部外(比如 Connection, User-Agent,或者其他任意的Fetch规范定义的“禁止的头部名”),预请求不包括Fetch规范定义的“ CORS-safelistedrequest-header”: Accept Accept-Language Content-Language Content-Type(butnotetheadditionalrequirementsbelow)DPR Downlink Save-Data Viewport-Width Width Content-Type头部的值除了下列之外的:application/x-www-form-urlencodedmultipart/form-datatext/plain
注意:WebKitNightly和SafariTechnologyPreview添加了额外的限制在 Accept, Accept-Language,and Content-Language头部的值。如果这些头部的值是非标准的,WebKit/Safari就不会将这些请求视为“简单请求”。WebKit/Safari并没有将非标准的头部值列个文档出来,但是可以在WebKitbug里体现:Requirepreflightfornon-standardCORS-safelistedrequestheadersAccept,Accept-Language,andContent-Language, AllowcommasinAccept,Accept-Language,andContent-LanguagerequestheadersforsimpleCORS,and SwitchtoablacklistmodelforrestrictedAcceptheadersinsimpleCORSrequests。其它的浏览器并没有这个限制的实现,因为这个不在规范的内容里。
示例如下:varinvocation=newXMLHttpRequest();
varurl='http://bar.other/resources/post-here/';
varbody='<?xmlversion="1.0"?><person><name>Arun</name></person>';
functioncallOtherDomain(){
if(invocation)
{
invocation.open('POST',url,true);
invocation.setRequestHeader('X-PINGOTHER','pingpong');
invocation.setRequestHeader('Content-Type','application/xml');
invocation.onreadystatechange=handler;
invocation.send(body);
}
}
......
如上,以XMLHttpRequest创建了一个POST请求,为该请求添加了一个自定义请求头(X-PINGOTHER:pingpong),并指定数据类型为application/xml。所以,该请求是一个“预请求”形式的跨站请求。
让我们看看服务器与浏览器之间具体的交互过程:[code;'>OPTIONS/resources/post-here/HTTP/1.1
Host:bar.other
User-Agent:Mozilla/5.0(Macintosh][code=language-html]Access-Control-Request-Method:POST
Access-Control-Request-Headers:X-PINGOTHER
请求头Access-Control-Request-Method可以提醒服务器跨站请求
将使用POST方法,而请求头Access-Control-Request-Headers则告知服务器该跨站请求将携带一个自定义请求头
X-PINGOTHER。这样,服务器就可以决定,在当前情况下,是否接受该跨站请求访问。
第15至27行是服务器的响应。该响应表明,服务器接受了客服端的跨站请求。具体可以看看第18至21行:
Access-Control-Allow-Origin:http://foo.exampleAccess-Control-Allow-Methods:POST,GET,OPTIONS
Access-Control-Allow-Headers:X-PINGOTHER
Access-Control-Max-Age:1728000
响应头
Access-Control-Allow-Methods表明服务器可以接受
POST,
GET和
OPTIONS的请求方法。请注意,这个响应头类似于
Access-Control-Allow-Headers则表示服务器接受自定义请求头就像[code]Access-Control-Allow-Methods一样,X-PINGOTHER。
附带凭证信息的请求
XMLHttpRequest和访问控制功能,最有趣的特性就是,发送凭证请求(HTTPCookies和验证信息)的功能。一般而言,对于跨站请求,浏览器是不会发送凭证信息的。但如果将
XMLHttpRequest的一个特殊标志位设置为true,浏览器就将允许该请求的发送。
http://foo.example站点的脚本向值。脚本代码如下:http://bar.other站点发送一个GET请求,并设置了一个Cookies
functioncallOtherDomain(){
if(invocation){
invocation.open('GET',url,true);
invocation.withCredentials=true;
invocation.onreadystatechange=handler;
invocation.send();
}
如你所见,第七行代码将
withCredentials标志设置为true,从而使得Cookies可以随着请求发送。因为这是一个简单的GET请求,所以浏览器不会发送一个“预请求”。但是,如果服务器端的响应中,如果没有返回
Access-Control-Allow-Credentials:true的响应头,那么浏览器将不会把响应结果传递给发出请求的脚本程序,以保证信息的安全。
客户端与服务器端交互示例如下:
Host:bar.other
User-Agent:Mozilla/5.0(Macintosh]查看这里.下一部分将讨论实际的HTTP头信息.
HTTP响应头EDIT
这部分里列出了跨域资源共享(Cross-OriginResourceSharing)时,服务器端需要返回的响应头信息.上一部分内容是这部分内容在实际运用中的一个概述.Access-Control-Allow-Origin
返回的资源需要有一个Access-Control-Allow-Origin头信息,语法如下:Access-Control-Allow-Origin:<origin>|*
origin参数指定一个允许向该服务器提交请求的URI.对于一个不带有credentials的请求,可以指定为'*',表示允许来自所有域的请求.
举个例子,允许来自
Access-Control-Allow-Origin:'target='_blank'>http://mozilla.com[/code]如果服务器端指定了域名,而不是'*',那么响应头的Vary值里必须包含Origin.它告诉客户端:响应是根据请求头里的Origin的值来返回不同的内容的. Access-Control-Expose-Headers
RequiresGecko2.0(Firefox4/Thunderbird3.3/SeaMonkey2.1)
设置浏览器允许访问的服务器的头信息的白名单:Access-Control-Expose-Headers:X-My-Custom-Header,X-Another-Custom-Header
这样,X-My-Custom-Header和X-Another-Custom-Header这两个头信息,都可以被浏览器得到.Access-Control-Max-Age
这个头告诉我们这次预请求的结果的有效期是多久,如下:Access-Control-Max-Age:<delta-seconds>delta-seconds参数表示,允许这个预请求的参数缓存的秒数,在此期间,不用发出另一条预检请求.Access-Control-Allow-Credentials
告知客户端,当请求的credientials属性是true的时候,响应是否可以被得到.当它作为预请求的响应的一部分时,它用来告知实际的请求是否使用了credentials.注意,简单的GET请求不会预检,所以如果一个请求是为了得到一个带有credentials的资源,而响应里又没有Access-Control-Allow-Credentials头信息,那么说明这个响应被忽略了.Access-Control-Allow-Credentials:true|false
带有credential的请求在上面讨论. Access-Control-Allow-Methods
指明资源可以被请求的方式有哪些(一个或者多个).这个响应头信息在客户端发出预检请求的时候会被返回.上面有相关的例子.Access-Control-Allow-Methods:<method>[,<method>]*
发出预检请求的例子见上,这个例子里就有向客户端发送Access-Control-Allow-Methods响应头信息. Access-Control-Allow-Headers
也是在响应预检请求的时候使用.用来指明在实际的请求中,可以使用哪些自定义HTTP请求头.比如Access-Control-Allow-Headers:X-PINGOTHER
这样在实际的请求里,请求头信息里就可以有这么一条:[code;'>X-PINGOTHER:pingpong
可以有多个自定义HTTP请求头,用逗号分隔.
Access-Control-Allow-Headers:<]>[,<field-name>]*
HTTP请求头EDIT
这部分内容列出来当浏览器发出跨域请求时可能用到的HTTP请求头.注意这些请求头信息都是在请求服务器的时候已经为你设置好的,当开发者使用跨域的XMLHttpRequest的时候,不需要手动的设置这些头信息.Origin
表明发送请求或者预请求的域Origin:<origin>
参数origin是一个URI,告诉服务器端,请求来自哪里.它不包含任何路径信息,只是服务器名.
Note:Origin的值可以是一个空字符串,这是很有用的.
注意,不仅仅是跨域请求,普通请求也会带有ORIGIN头信息.
Access-Control-Request-Method
在发出预检请求时带有这个头信息,告诉服务器在实际请求时会使用的请求方式Access-Control-Request-Method:<method>
相关的例子可以在
Access-Control-Request-Headers
在发出预检请求时带有这个头信息,告诉服务器在实际请求时会携带的自定义头信息.如有多个,可以用逗号分开.Access-Control-Request-Headers:<field-name>[,<field-name>]*
相关的例子可以在
浏览器支持EDIT
DesktopMobile
Feature | Chrome | Firefox(Gecko) | InternetExplorer | Opera | Safari |
---|---|---|---|---|---|
Basicsupport | 4 | 3.5 | 8(viaXDomainRequest) 10 | 12 | 4 |
注意:
InternetExplorer8和9通过XDomainRequest对象来实现CORS,但是在IE10中有完整的实现。Firefox3.5就引入了对跨站XMLHttpRequests和Web字体的支持,尽管存在着一些直到后续版本才取消的限制。特别的,Firefox7引入了对跨站WebGL纹理的HTTP请求的支持,而且Firefox9添加对通过drawImage在canvas上绘图的支持。相关链接EDIT
XMLHttpRequestandCross-OriginResourceSharing
XMLHttpRequest
相关文章推荐
- HTTP访问控制(CORS)
- HTTP访问控制(CORS)
- HTTP 访问控制(CORS)
- HTTP访问控制(CORS)
- HTTP访问控制(CORS) 跨域访问
- HTTP访问控制(CORS)
- HTTP 错误 401.3 - Unauthorized 由于 Web 服务器上此资源的访问控制列表(ACL)配置或加密设置,您无权查看此目录或页面。
- IIS中遇到无法预览的问题(HTTP 错误 401.3 - Unauthorized 由于 Web 服务器上此资源的访问控制列表(ACL)配置或加密设置,您无权查看此目录或页面。)
- http2.4.10 Option, IP访问控制
- 外网IP通过路由器映射内网某台地址IP(http直接访问、远程桌面控制)
- 阿里 SLB +nginx 基于$http_x_forwarded_for 代理IP 访问控制
- Nginx+lua+http_checkmodule+tcp_checkmodule+动态监测与访问控制 安装
- webapi使用System.Web.Http.Cors配置跨域访问的几点注意事项
- 基于角色访问控制的UML表示(http://www.mmmbook.com/xprogrammer/issue/25/rolebased.htm)
- System.Web.Http.Cors配置跨域访问的两种方式
- nginx访问控制:如何通过map来控制http_x_forwarded_for访问限制
- 外网IP通过路由器映射内网某台地址IP(http直接访问、远程桌面控制)
- webapi使用System.Web.Http.Cors配置跨域访问的几点注意事项
- HttpModule实现系统IP访问控制