Hu3sky's blog

跨域相关问题

Word count: 1,970 / Reading time: 9 min
2019/04/01 49 Share

同源策略

同源策略是一种约定,它是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能会收到影响,可以说Web是构建在同源策略上的,浏览器只是针对同源策略的一种实现

来自不同源的对象无法相互干扰
比如a.com带着cookie访问b.com,而b.com能读取到a.com的cookie,所以为了避免这种情况产生,就有了同源策略
使用jQuery向不同源目标站发送请求

1
$.post("http://hu3sky.ooo")

1

这里的Access-Control-Allow-Origin头表示服务端允许哪些源的请求

以下情况被认为是不同源的

1
2
3
4
5
6
7
8
9
10
11
不同协议:
http://hu3sky.ooo
https://hu3sky.ooo

不同端口
http://hu3sky.ooo:80
http://hu3sky.ooo:81

不同域
http://a.hu3sky.ooo
http://b.hu3sky.ooo

只有当域名,端口,协议都相同,才会被认为是同源

1
2
http://hu3sky.ooo/a.php
http://hu3sky.ooo/b.php

而在IE中,未将端口号划分进来,也即是说http://hu3sky.ooo:81/index.htmlhttp://hu3sky.ooo/index.html 属于同源并且不受任何限制。

同源策略的限制

在非同源的情况下,会受到限制
限制之一就是不能通过ajax的方法去请求不同源中的文档,比如XMLHttpRequest方法,只能请求同源对象的内容。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的

跨域标签

<img>,<link>,<iframe>,<script>等利用src,href属性都可以跨域加载资源,不受同源策略的限制

跨域资源共享的方式

document.domain

适用范围:

  • 两个域的子域不同
  • 只适用于iframe不同窗口之间互相获取cookie和DOM节点(
    如果文档包含框架(frame 或 iframe 标签),浏览器会为 HTML 文档创建一个 window 对象,并为每个框架创建一个额外的 window 对象)

当两个不同的域只是子域不同时,可以通过把document.domain设置为他们共同的父域来解决

example

a.hu3sky.com/1.html

1
<iframe src=" http://b.hu3sky.com/2.html" id="iframe">

b.hu3sky.com/2.html

1
<h1>Hello!</h1>

在a.hu3sky.com的窗口下获取iframe里的document对象的name属性,会报错
1

接下来,我们添加document.domain

a.hu3sky.com/1.html

1
2
<script>document.domain = 'hu3sky.com'</script>
<iframe src="http://b.hu3sky.com/2.html" id="iframe" />

b.hu3sky.com/2.html

1
2
<script>document.domain = 'hu3sky.com'</script>
<h1>Hello!</h1>

并未报错
1

注意:

  • 即使你设置了相同的document.domain,也无法通过AJAX去请求b页面的,该方法只适用于iframe不同窗口之间互相获取cookieDOM节点
  • 设置的document.domain域名的级别不能低于当前的域名,比如只能设置为a.hu3sky.comhu3sky.com
  • 在同窗体下,即使不设置document.domain其实也可以获得window对象,不过可用的属性非常少,几乎没用

JSONP

在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的

example

a.hu3sky.com/get_data.html

1
2
3
4
5
6
7
8
9
10
11
<html>
<head>
<script>
function func(jsondata)
{
alert("json data: "+jsondata); //处理回调的json数据
}
</script>
<script src="http://b.hu3sky.com/json_data.php?callback=func" ></script>
</head>
</html>

b.hu3sky.com/json_data.php

1
2
3
4
5
<?php
header('Content-type: application/javascript');
$callback = $_GET['callback'];
$data = '["hack","the","world"]';
echo $callback . "(" . json_encode($data) . ")";

1

  1. <script>标签用在json_data.php中定义好的callback参数来请求到数据
  2. json_data.php把数据当函数参数传入返回给get_data.html
  3. get_data.html中定义了func函数来处理获取的参数

关于header('Content-type: application/javascript');
先是设置了header的情况
1
并不会造成XSS
没有设置header的时候
1
但是在设置了的情况下,依然可能造成XSS
在application/json,application/javascript等Response下进行XSS

window.name

window对象有个name属性,一个窗口的生命周期内的页面都是共享一个window.name的,每个页面对window.name都有读写权限
即使跳转,也会保留,例如在https://cn.bing.com/的console
1
接着在当前窗口进行跳转
location.href = "www.baidu.com"
1
window.name的值为String

example

a.hu3sky.com/a.html

1
2
3
<script type = "text/javascript">
window.name = "aaaaaaa";
</script>

b.hu3sky.com/b.html

1
<iframe src = "http://a.hu3sky.com/a.html" id = "iframe"></iframe>

c.hu3sky.com/c.html

1
2
3
<script>
alert(window.name);
</script>

1

原理:

  1. a.html设置window.name
  2. b.html利用iframe获取a.html的window对象,前面讲过iframe由于是跨域,不能获取到name属性
  3. 在b.htm通过设置iframel src为c.html,在iframe中的所有页面共享window.name,于是获取到name属性

window.postMessage(HTML5)

window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,该函数的第一个参数为发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 *

example

aaa.html

1
2
3
<script>
<iframe src = "http://b.hu3sky.com/bbb.html" id = "iframe">
</script>

bbb.html

1
2
3
4
5
6
7
8
<script>
//onmessage 属性是当对象接收到message 事件时被调用的EventHandler .
window.onmessage = function(e)
{
e = e || event;
alert(e.data);
}
</script>

在控制台发送

1
iframe.contentWindow.postMessage("alert","*")

获取iframe的window对象,调postMessage方法
1
当没对bbb.html进行处理时,可能会造成XSS
aaa.html

1
<iframe src = "http://b.hu3sky.com/bbb.php" id = "iframe"></iframe>

bbb.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
setcookie("password","Adif9gjsk_asda");
?>
<p id="name">aa</p>
<script>
window.onmessage = function(e)
{
e = e || event;
document.getElementById('name').innerHTML = e.data;
}

</script>

控制台发送
iframe.contentWindow.postMessage("<img src=1 onerror=alert(document.cookie)>","*")

1

CORS

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。
它允许浏览器跨域发送AJAX,例如XMLHttpRequest请求

相比JSONP只能发送get请求,CORS允许发送任何类型的请求。但CORS要求浏览器和服务器同时支持。目前所有浏览器都支持,IE需要IE10以上。

example

aaaa.php

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
<html>
<head>
<script type="text/javascript">
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","http://b.hu3sky.com/bbbb.html",true);
xmlhttp.send();
}
</script>
<button type="button" onclick="loadXMLDoc()">请求数据</button>

bbbb.php

1
<h2>AJAX</h2>

当没有设置CORS头时,看到,我们用AJAX去跨域请求数据
1
报错如下

1
已拦截跨源请求:同源策略禁止读取位于 http://b.hu3sky.com/bbbb.html 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。

当我们在bbbb.php加了CORS头

1
2
3
4
<?php
header("Access-Control-Allow-Origin:http://a.hu3sky.com");
?>
<h2>AJAX</h2>

再次访问,就能够请求到数据了

1

观察请求包和返回包
1

浏览器发现它是简单请求,就会直接在头信息中加一个origin字段
服务器收到这条请求,如果这个origin指定的源在许可范围内,那么服务器返回的头信息中会包含Access-Control-Allow-Origin字段,值与origin的值相同

如果请求需要带上Cookie,则需要服务器设置Access-Control-Allow-Credentials: true否则浏览器将不会把响应内容返回给请求的发送者

注意
当设置Access-Control-Allow-Origin=*时,浏览器处于安全性考虑,即使设置了Access-Control-Allow-Credentials: true,也不向服务端发送cookie

Referers

Error: Not Found
CATALOG
  1. 1. 同源策略
  2. 2. 跨域资源共享的方式
  3. 3. Referers