同源策略(Same-Origin-Policy,SOP)javascript
同源策略是一种约定,是浏览器最核心也最基本的一个安全功能,不一样源的客户端脚本在没有明确受权的状况下,不能读写对方资源。好比a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的。若是缺乏了同源策略,则浏览器的正常功能均可能会受到影响。能够说Web是构建在同源策略基础上的,浏览器只是针对同源策略的一种实现。html
同源策略限制了从同一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。前端
为何要有同源策略java
浏览器主要是从两个方面去作同源策略的,一是针对接口的请求,二是针对DOM的查询。ajax
接口请求上的同源策略json
有一个小小的东西叫cookie你们都知道,通常是用来处理登录等场景,目的是让服务端知道请求是谁发出的。若是你请求了接口进行登录,服务端验证经过以后就会在响应头(Response Headers)加入Set-Cookie属性,而后下次再发请求的时候,浏览器就会自动将cookie附加在HTTP请求头(Request Headers)的cookie属性中,服务端就能知道这个用户已经登录过了。后端
知道了cookie的工做原理后,咱们来看没有同源策略的场景:api
1.你准备去剁手,因而打开了买买买网站,登录,选了一些东西到购物车。跨域
2.你还没选够东西呢,忽然弹出来一个连接,你一点,跳到了骗骗骗网站。浏览器
3.骗骗骗网站吸引住你了,你饶有兴趣地浏览着,殊不知道这个网站在暗地里作了坏事:它向买买买网站发起了请求!由于你登录和浏览买买买网站的时候已经产生了cookie在浏览器中,那骗骗骗网站由于没有同源策略的限制,就能够偷偷拿到这个cookie,就至关于登录了你买买买网站的帐号,而后随心所欲,偷偷地给你买一堆面膜。
这就是传说中的CSRF(Cross-site Request Rorgery,跨站请求伪造)攻击。
DOM查询上的同源策略
有一天你收到一条短信,说是你的银行帐号有风险,要你赶忙点进www.yinghang.com更改密码。你吓尿了,卡里有三十多块钱呢,因而你果断点进去,看到仍是熟悉的银行登录界面,果断地输入你的帐号密码。着急的你却没有看清楚,其实平时访问的是www.yinhang.com。那么问题来了,这个钓鱼网站究竟会作什么呢?
首先,它会在网页中内嵌一个iframe,iframe中是真的www.yinhang.com。
<iframe name="yinhang" src="www.yinhang.com"></iframe>
而后,由于没有同源策略限制,它就能拿到www.yinhang.com网站的DOM。
window.frames['yinhang'].document.getElementById('你输入帐号密码的input').val();
经过DOM去获取输入的帐号密码,而后取走你卡里的三十多块钱,让你痛不欲生。
综上所述,同源策略是能有效规避一些风险的。但不是说有了同源策略就必定安全的,只是说同源策略是浏览器的一种最基本的安全机制,毕竟能提升一点攻击成本。事实上,没有刺不穿的盾,只有攻击的成本和攻击成功后得到收益的正比。
源(Origin)
咱们知道,URL(统一资源定位符)是由协议、域名、端口和路径组成的。一般理解的源,就是指的是URL中的协议、域名和端口的组合。
既然URL是描述资源的,那么源也是描述资源的一个部分。
这样理解,静静是个来自山西的女孩子,芳芳是个来自广东的女孩子。描述静静的时候,就会说她是来自山西的静静;描述芳芳的时候,就会说她是来自广东的芳芳。这个来自哪里,就是源。
同源(Same-Origin)
若是两个URL的协议、域名和端口相同,则称它们是同源的。
莉莉也是来自广东的女孩子,这时就能够说她和来自广东的芳芳是同源的。
跨域(Cross-Origin)
与同源相反的,只要协议、域名和端口三个组成中有任何一个不一样,就视为不一样源。
从一个源(预)去请求不一样源(域)的资源,就称为跨域。
浏览器中有一些不受同源限制的标签,好比说<script>、<img>、<iframe>、<link>这些包含src属性的标签能够加载跨域的资源。可是浏览器限制了JavaScript的权限使其不能读或写这些标签内加载的内容。
如何解开JavaScript的限制去读、写这些标签内加载的内容,实际上就是咱们要解决的跨域问题。
跨域的解决方案
跨域主要有几个解决方案:降域、postMessage、JSONP、CORS和反向代理等。
同源策略限制下接口请求的跨域解决方案:JSONP、CORS和反向代理等。
JSONP
前面提到,在HTML标签里一些标签,好比说<script>、<img>这样带src属性的获取资源的标签,是没有跨域限制的。利用这一点,咱们就能够干点坏事,呸,好事。
如今有个a.com/jsonp.html想要获得b.com/main.js中的数据,就能够在a.com的jsonp.html里面建立一个回调函数callback1,动态地添加<script>元素,而后向服务器发送请求,在请求地址后面加上查询字符串,最后经过callback参数指定回调函数的名字。
这时的请求地址就是b.com/main.ja?callback=callback1。在main.js中调用这个回调函数callback1,而且以JSON数据的形式做为参数传递,完成回调。咱们来看看代码:
/* a.com/jsonp.html中的代码 */ // 建立script标签 function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type", "text/javascript"); script.src = src; document.body.appendChild(script); } // 页面加载完成后再执行 window.onload = function () { addScriptTag("http://b.com/main.js?callback=foo"); } // 回调函数 function foo(data) { console.log(data.name + "好可爱"); };
/* b.com/main.js中的代码 */ foo({name:"静静"}); // 调用回调函数,把要的东西传给它
这样,就完成了跨域的参数传递。
JSONP的注意事项:
1.JSONP和JSON看起来很类似,可是其实他们之间没有半毛钱关系。
2.使用这种方式的话,只要是个网站都能拿到b.com里的数据,存在安全性问题,须要网站双方商议基础token的身份验证。
3.JSONP只能是GET的方式,不支持POST等其它方式。
4.JSONP可能在回调函数中被注入恶意代码,篡改页面内容。能够采用字符串过滤的方法规避此问题。
5.jQuery或AngularJs中有提供封装好的jsonp方法。
CORS(CrossOrigin Resource Sharing, 跨源资源共享)
CORS是一个W3C标准,看名字就知道这时处理跨域问题的标准作法。CORS有两种请求,简单请求(Simple Request)和非简单请求(Not-so-simple Request)。
简单请求
同时知足如下两个条件,就属于简单请求:
1.请求方法必须是HEAD、GET、POST三种方法之一。
2.HTTP的头信息不能多于Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只限application/x-www-form-urlencoded、mltipart/form-data、text/plain三个值)这些字段。
非简单请求
非简单请求会发出一次预检测请求,返回码是204,预检测经过以后才会发真正的请求,这才返回200。能够经过前端发请求的时候增长一个额外的headers来触发非简单请求。
CORS的实现方式比较复杂,要用另外的篇幅去叙述,这里就不作叙述了(懒,哈哈哈哈)。
反向代理
试想一下,若是咱们请求的时候仍是用前端的域名,可是当提交到后台的时候,有个东西帮咱们把这个请求转发到真正的后端域名上,不就避免跨域了吗?当前很火的Nginx就是一个能解决跨域问题的反向代理应用。
Nginx的配置
server{ # 监听9009端口 listen 9009; # 域名是localhost server_name localhost; # 凡是localhost:9009/api这个样子的,都转发到真正的服务端地址localhost:9888上去 location ^~ /api { proxy_pass http://localhost:9888; } }
前端的请求
http://localhost:9009/api/dosomething
前端请求的时候直接使用前端这边的域名localhost:9009便可,这样就是同源了,不会有跨域的问题。Nginx监听到凡是localhost:9009/api这样的,都转发到真正的服务端地址localhost:9888上。
Nginx转发请求的方式彷佛很方便,可是这种使用是要看场景的。好比说若是后端接口是一个公共服务的API,去获取天气什么的,前端调用的时候总不能让运维去配Nginx吧。若是浏览器兼容性没问题的话(IE10或以上),CORS才是更通用的作法。
同源策略限制下DOM查询的跨域解决方案:降域、postMessage等。
降域(Descending Domain)
同源策略认为域和子域属于不一样的域。好比说:
child1.parent.com与parent.com不一样源
child1.parent.com与child2.parent.com不一样源
grandson1.child1.parent与child1.parent.com不一样源
能够经过设置document.domain = 'parent.com'让浏览器认为他们都属于同一个源。想要实现以上任意两个页面之间的通讯,两个页面都必须这样设置。
降域的特色与注意事项:
1.降域主要针对于Cookie和当前页面下的iframe。
2.降域只能在父域名和子域名之间使用,即只适合在同一主域名下使用。且将grandson1.child1.parent.com的document.domain设置为parent.com以后,不能再设置成child1.parent.com。
3.降域须要有降的空间,顶级域名没法降域。
4.降域是要双向设置document.domain的,不只当前页面要设置,iframe内嵌的页面也要设置。
5.降域存在安全性问题,当一个站点被攻击后,另外一个站点也会引发安全漏洞。
postMessage
window.postMessage()是HTML5的一个接口,专一实现不一样窗口不一样页面的跨域通信。
具体使用这里就不作叙述了(仍是懒,嘿嘿)。
"别说了,我想静静。"
"静静是谁?"