根据同源策略,浏览器默认是不允许XMLHttpRequest对象问非同一站点下的资源的,即用ajax方式访问非同一域名下的资源会出错。比如当google要通过ajax去访问百度的数据,是不行的。
所谓同源,是要求协议,域名,端口都相同。
比如 http://www.aaa.com 和下列URL相比,都不属于同源。
但下面这种属于同源:
http://username:password@www.aaa.com
禁用跨域访问资源是为了安全,但会牺牲便利性。因此就出现了好几种跨域访问资源的方法。其中一种是称之为CORS的技术(Cross-origin resource sharing)。
CORS的概念
所谓CORS(Cross-origin resource sharing ),是指通过XMLHttpRequest的ajax方式访问其他域名下资源,而不是在A域名的页面上点击打开一个B域名的页面。引入不同域上的js脚本文件也是没用问题的。出于安全考虑,跨域请求不能访问document.cookie对象。
CORS技术允许跨域访问多种资源,比如javascript,字体文件等,这种技术对XMLHttpRequest做了升级,使之可以进行跨域访问。但不是所有的浏览器都支持CORS技术。Firefox和Chrome等浏览器支持的比较好,稍微新一点的版本都支持。IE比较搓,IE10才真正支持这个机制,IE10以下需要用XDomainRequest这个对象,这是IE特有的。
当然不是说浏览器支持了就立刻可以跨域访问了,CORS技术中最重要的关键点是响应头里的Access-Control-Allow-Origin这个Header。 此Header是W3C标准定义的用来检查是可否接受跨域请求的一个标识。实现过程大致如下:A域名中发起跨域请求到B域名,B域名如果发送响应头Access-Control-Allow-Origin: A域名,那么A域名的这次跨域请求就能成功。反之则不成功。这里有一个称之为Preflighted requests 的步骤,这一步骤中,浏览器发起一个OPTIONS请求,去服务器验证是否支持跨域访问。(
用chrome去查看这个option请求始终看不到,只有当跨域访问请求非正常(比如本人笔误,将请求type的get误写成了gey)的情况下,才会看到有个OPTIONS请求,如图:
非常奇怪,不知道为何。
CORS的实现
举个例子。
有2个站点,分别绑定了www.myhost1.com和www.myhost2.com:8001这个两个域名。
假设www.myhost2.com:8001下的a.html文件,需要访问www.myhost1.com域名下的b.aspx文件,代码如下。
a文件代码如下,点击按钮就会去跨域访问b页面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<!DOCTYPE html>
<
html
lang
=
"en"
xmlns="
http://www.w3.org/1999/xhtml
">
<
head
>
<
meta
charset
=
"utf-8"
/>
<
title
>Apage</
title
>
<
script
type
=
"text/javascript"
src="
http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js
"></
script
>
<
script
>
$(function () {
var options = {
type: 'get',
url: "
http://www.myhost1.com/mysite/cors/b.aspx
",
success: function (result) {
alert(result);
}
};
$("#btn1").click(function () {
$.ajax(options);
});
});
</
script
>
</
head
>
<
body
>
<
input
type
=
"button"
id
=
"btn1"
value
=
"load page b"
/>
</
body
>
</
html
>
|
b文件为一个aspx文件,代码非常简单,显示时间
1
2
|
<%@ Page Language=
"C#"
%>
<%=DateTime.Now%>
|
A文件的地址为http://www.myhost2.com:8001/a.html
A文件的请求是按照get方式读取的,当A文件点击按钮,会弹出B文件的内容。由于同源策略,会看到如下错误:
为了避免这个错误,在www.myhost1.com主机的Http响应标头里加上Access-Control-Allow-Origin:http://www.myhost2.com:8001,注意不要打上最后一个斜杠/,意思是允许http://www.myhost2.com:8001这个域名中的XMLHttpRequest对象跨域访问www.myhost1.com主机下的资源。
还有一种方法是修改web.config,Access-Control-Allow-Origin的值可以是通配符*,比如如下:
1
2
3
4
5
6
7
8
9
10
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
configuration
>
<
system.webServer
>
<
httpProtocol
>
<
customHeaders
>
<
add
name
=
"Access-Control-Allow-Origin"
value
=
"*"
/>
</
customHeaders
>
</
httpProtocol
>
</
system.webServer
>
</
configuration
>
|
或者通过代码添加响应标头来实现。
1
2
3
4
|
<%@ Page Language=
"C#"
%>
<%
Response.AppendHeader(
"Access-Control-Allow-Origin"
,
"http://www.myhost2.com:8001"
); %>
<%=DateTime.Now%>
|
当浏览器通过ajax请求访问其他域名下的资源时,如果那个资源的响应头中包含了Access-Control-Allow-Origin,则可以请求成功,否则就会失败,是否设置了响应头,可以在firebug等调试工具内看到。
无响应头
有响应头
跨域访问的风险
默认情况下,CORS机制不读取cookie值,即跨域访问时没有带上cookie,当需要跨域访问带cookie时,需要设置另一个文件头,Access-Control-Allow-Credentials,值为true。该选项设定了是否允许跨域请求带cookie。当设定为true后,对于XMLHttpRequest还需要设定withCredentials为true,在Jquery中为xhrFields: {withCredentials: true}
一旦允许了带cookie的跨域访问,那么遭到CSRF攻击的概率大大的增加了,比如之前的假设a.html页面内有一段恶意的js,它每隔1分钟去跨域请求用户b页面,并将请求后的数据偷偷的存入自己的数据库内。假设b页面需在要用户登录的情况下,可以请求到很多机密数据,比如信用卡号等,那当用户打开a页面后,在非授意的情况下,获取到b面值的内容,引发泄密。
对之前的演示代码做小的修改:
a页面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!DOCTYPE html>
<
html
lang
=
"en"
xmlns
=
"http://www.w3.org/1999/xhtml"
>
<
head
>
<
meta
charset
=
"utf-8"
/>
<
title
>Apage</
title
>
<
script
type
=
"text/javascript"
src
=
"http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"
></
script
>
<
script
>
$(function
() {
var options = {
type: 'get',
xhrFields: {withCredentials: true},
url: "http://www.myhost1.com/mysite/cors/b.aspx",
success:
function (result) {
alert(result);
}
};
$("#btn1").click(function ()
{
$.ajax(options);
});
});
</
script
>
</
head
>
<
body
>
<
input
type
=
"button"
id
=
"btn1"
value
=
"load page b"
/>
</
body
>
</
html
>
|
b.aspx页面代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<%@ Page Language=
"C#"
AutoEventWireup=
"true"
CodeBehind=
"b.aspx.cs"
%>
<%
Response.AppendHeader(
"Access-Control-Allow-Origin"
, "
http:
//www.myhost2.com:8001
");
Response.AppendHeader(
"Access-Control-Allow-Credentials"
,
"true"
);
if
(Session[
"islog"
] !=
null
)
{
if
(Session[
"islog"
] ==
"1"
)
{
Response.Write(
"已登录,显示机密信息 "
);
}
}
else
{
Response.Write(
"未登录"
);
}
if
(Request[
"login"
] ==
"true"
)
{
Session[
"islog"
] =
"1"
;
Session.Timeout = 1;
Response.Write(
"Login in OK"
);
}
%>
<%=DateTime.Now%>
|
假设b页面的用户登录了一下,种植了一个session,那么就会有一个sessionid被存储到用户的cookie中去,此时,a页面点击按钮后,会获取b页面显示的内容,根据黑客的行为,可以有更大的破坏力。
演示图:当b页面未登录时,显示未登录。请求中不带cookie。
但是当b页面已经登录,则在a页面中的跨域请求中,带有cookie,会显示已登录的页面。
参考资料:
http://drops.wooyun.org/tips/188
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
https://dev.opera.com/articles/dom-access-control-using-cors/
http://blog.darkthread.net/post-2014-09-29-cors-options-preflight-and-iis.aspx
http://www.cnblogs.com/idche/p/3190926.html
本文转自cnn23711151CTO博客,原文链接:http://blog.51cto.com/cnn237111/1610917 ,如需转载请自行联系原作者