同源策略
同源策略是一种约定,它是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能会收到影响,可以说Web是构建在同源策略上的,浏览器只是针对同源策略的一种实现
来自不同源的对象无法相互干扰
比如a.com
带着cookie访问b.com
,而b.com
能读取到a.com
的cookie,所以为了避免这种情况产生,就有了同源策略
使用jQuery向不同源目标站发送请求
1 | $.post("http://hu3sky.ooo") |
这里的Access-Control-Allow-Origin头表示服务端允许哪些源的请求
以下情况被认为是不同源的
1 | 不同协议: |
只有当域名,端口,协议都相同,才会被认为是同源
1 | http://hu3sky.ooo/a.php |
而在IE中,未将端口号划分进来,也即是说http://hu3sky.ooo:81/index.html
和 http://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属性,会报错
接下来,我们添加document.domain
a.hu3sky.com/1.html
1 | <script>document.domain = 'hu3sky.com'</script> |
b.hu3sky.com/2.html
1 | <script>document.domain = 'hu3sky.com'</script> |
并未报错
注意:
- 即使你设置了相同的
document.domain
,也无法通过AJAX
去请求b页面的,该方法只适用于iframe
不同窗口之间互相获取cookie
和DOM
节点 - 设置的
document.domain
域名的级别不能低于当前的域名,比如只能设置为a.hu3sky.com
或hu3sky.com
- 在同窗体下,即使不设置
document.domain
其实也可以获得window对象,不过可用的属性非常少,几乎没用
JSONP
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的
example
a.hu3sky.com/get_data.html
1 | <html> |
b.hu3sky.com/json_data.php
1 | <?php |
<script>
标签用在json_data.php
中定义好的callback
参数来请求到数据json_data.php
把数据当函数参数传入返回给get_data.html
get_data.html
中定义了func
函数来处理获取的参数
header
关于header('Content-type: application/javascript');
先是设置了header的情况
并不会造成XSS
没有设置header的时候
但是在设置了的情况下,依然可能造成XSS
在application/json,application/javascript等Response下进行XSS
window.name
window对象有个name属性,一个窗口的生命周期内的页面都是共享一个window.name
的,每个页面对window.name
都有读写权限
即使跳转,也会保留,例如在https://cn.bing.com/的console
接着在当前窗口进行跳转location.href = "www.baidu.com"
window.name
的值为String
型
example
a.hu3sky.com/a.html
1 | <script type = "text/javascript"> |
b.hu3sky.com/b.html
1 | <iframe src = "http://a.hu3sky.com/a.html" id = "iframe"></iframe> |
c.hu3sky.com/c.html
1 | <script> |
原理:
a.html
设置window.name
- b.html利用iframe获取a.html的window对象,前面讲过iframe由于是跨域,不能获取到name属性
- 在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 | <script> |
bbb.html
1 | <script> |
在控制台发送
1 | iframe.contentWindow.postMessage("alert","*") |
获取iframe的window对象,调postMessage方法
当没对bbb.html进行处理时,可能会造成XSS
aaa.html
1 | <iframe src = "http://b.hu3sky.com/bbb.php" id = "iframe"></iframe> |
bbb.php
1 | <?php |
控制台发送iframe.contentWindow.postMessage("<img src=1 onerror=alert(document.cookie)>","*")
CORS
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。
它允许浏览器跨域发送AJAX,例如XMLHttpRequest请求
相比JSONP只能发送get请求,CORS允许发送任何类型的请求。但CORS要求浏览器和服务器同时支持。目前所有浏览器都支持,IE需要IE10以上。
example
aaaa.php
1 | <html> |
bbbb.php
1 | <h2>AJAX</h2> |
当没有设置CORS头时,看到,我们用AJAX去跨域请求数据
报错如下
1 | 已拦截跨源请求:同源策略禁止读取位于 http://b.hu3sky.com/bbbb.html 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。 |
当我们在bbbb.php加了CORS头
1 | <?php |
再次访问,就能够请求到数据了
观察请求包和返回包
浏览器发现它是简单请求,就会直接在头信息中加一个origin字段
服务器收到这条请求,如果这个origin指定的源在许可范围内,那么服务器返回的头信息中会包含Access-Control-Allow-Origin
字段,值与origin的值相同
如果请求需要带上Cookie,则需要服务器设置Access-Control-Allow-Credentials: true
否则浏览器将不会把响应内容返回给请求的发送者
注意
当设置Access-Control-Allow-Origin=*
时,浏览器处于安全性考虑,即使设置了Access-Control-Allow-Credentials: true
,也不向服务端发送cookie