同源策略
同源定义
要素:协议、域名、端口
如果两个url的协议、域名和端口相同,那么这两个url同源,否则不同源。
示例:
URL 是否同源 原因 http://test.com/haha/1.html 参照 https://test.com/2.html 不同源 协议不同 http://test.com:8000/haha/3.html 不同源 端口不同 http://testtest.com/haha/4.html 不同源 主机不同 http://test.com/xixi/5.html 同源 http://test.com/haha/6.html 同源 注:域名和ip即使对应,也算不同源
同源策略
同源策略(Same Origin Policy)是一种约定,它是浏览器最核心也最基本的安全功能。它限制来自一个源的文档如何与不同源的资源进行交互。
同源策略的目的就是防止恶意网站窃取数据。假如用户访问A网站,登录之后Cookie产生,随即访问B网站,如果没有同源策略限制,B网站的资源则可以读取A网站的Cookie,危害产生。
限制范围
受策略限制的非同源行为:
- Cookie、localStorage和IndexedDB无法读取
- DOM无法获得
- AJAX请求可发送但是客户端接收不到返回信息
同源策略允许跨域写(发出请求),不允许跨域读(接收响应)
跨域实现
src属性的标签
在浏览器中,<script>、<img>、<iframe>、<link>
等标签都可以跨域加载资源,而不受同源策略的限制。这些带src
属性的标签每次加载时,实际上是由浏览器发起了一次 GET 请求。
注意:为了安全起见,浏览器不允许对以这种方式加载到的资源进行读写操作,而只能使用标签本身应当具备的能力(比如脚本执行、样式应用等等)。
CORS(跨域资源共享)
CORS允许浏览器向跨源服务器发出XMLHttpRequest请求,并且接收响应,克服了AJAX只能同源使用的限制。
当发出XMLHttpRequest请求时,浏览器会自动在HTTP请求头中加入Origin字段,服务端识别该字段并判断此"源(origin)“是否是合法的,如果合法则允许跨域资源访问。
示例:
- http://www.a.com/test.html 网站发起跨域的 ⅩMLHttpRequest 请求,请求地址为http://www.b.com/test.php。
- 请求头中加入Origin:
- 服务端校验该Origin Header(Origin Header 可以用于防范 CSRF,它不像 Referer 那么容易被伪造或清空),并设置响应头
Access-Control-Allow-Origin: *
,返回该响应。 Access-Control-Allow-Origin: *
意味着信任任何源的跨域请求(极其危险),浏览器接收到该响应,认为允许跨域,便不做拦截,客户端的跨域请求通过。
JSONP跨域
JSONP原理:
<script>
是可跨域的,而且在跨域脚本中可以通过传参直接回调当前客户端脚本定义的函数。
例如Google的AJAX搜索接口:
<script type="text/javascript">
//添加<script>标签的方法
function addScriptTag(src){
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function(){
//搜索apple,将自定义的回调函数名result传入callback参数中
addScriptTag("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=apple&callback=result");
}
//自定义的回调函数result
function result(data) {
//弹出'apple搜'索结果的第一条记录中url数据
alert(data.responseData.results[0].unescapedUrl);
}
</script>
设置document.domain
原理:
让两个不同的子域通过设置document.domain为同一个父域,借助iframe标签,实现两个子域的交互。
示例:
// http://a.test.com/a.html:加载窗口,设置父域
<iframe id = "iframe" src="http://b.test.com/b.html" onload = "haha()"></iframe>
<script type="text/javascript">
document.domain = 'test.com'; //设置父域
function haha(){
alert(document.getElementById('iframe').contentWindow);
//contentWindows属性返回子窗口的 window 对象,以此获取内部的DOM
}
</script>
// http://b.test.com/b.html:设置父域
<script type="text/javascript">
document.domain = 'test.com';
//当iframe加载这个页面时,也设置document.domain,使之与主页面的document.domain相同
</script>
window.postMesage方法
HTML5新增了postMessage方法:
postMessage
:otherWindow.postMessage(message, targetOrigin);
- otherWindow:指目标窗口,即向哪个window发送消息,可以是window.frames 属性的成员或者由 window.open 方法创建的窗口
- message:是要发送的消息,类型为 String、Object
- targetOrigin:是限定消息接收范围,不限制请使用
*
跨域原理:
iframe标签加载目标域内容,加载时通过postMesage传送消息,在目标域中通过监听message时间来获取消息。
可跨主域、双向跨域
示例:
// test.com/test.html:加载窗口并发送消息
<body>
<iframe id="proxy" src="http://target.com/remote.html" onload = "postMsg()" style="display: none"></iframe>
<script type="text/javascript">
var obj = {
msg: 'hahaha!'
}
function postMsg (){
var iframe = document.getElementById('proxy');
var win = iframe.contentWindow;
win.postMessage(obj,'http://target.com');
}
</script>
</body>
// http://target.com/remote.html:监听事件,获取消息
<head>
<title></title>
<script type="text/javascript">
window.onmessage = function(e){
if(e.origin !== 'http://localhost:8088') return;
alert(e.data.msg+" from "+e.origin);
}
</script>
</head>
CORS 与 CSRF异同
相同点:
- 都需要借助第三方网站
- 都需要借助 AJAX 的异步过程
- 一般都需要用户登录
不同点:
- 第三方网站可以利用 CORS 漏洞读取到受害者的敏感信息
- 第三方网站可以利用 CSRF 漏洞可以让受害者执行一些敏感操作
- 一般有 CORS 漏洞的地方也会有 CSRF 漏洞