同源策略
同源策略为JavaScript文件只能访问与其同源的资源和数据。同源策略浏览器最核心也是最基本的安全功能,其目的是为了防止恶意网站窃取数据。所谓同源是指协议、域名、端口都相同。IE关于同源不要求端口相同。
同源禁止的行为
考虑同源,要考虑三个部分:不同源的服务器、不同源的窗口和文档、不同源的存储数据。一般情况下,同源策略禁止以下行为:
- 禁止JavaScript访问不同源的Cookie、LocalStorage以及IndexDB等存储数据。
- 禁止读取和操作与JavaScript脚本不同源的窗口和文档的大多数属性。
- 禁止XMLHttpRequest生成的HTTP请求与不同源的服务器通信。(如果允许,就可以在控制台写个ajax提交到任意的服务器了)
同源策略允许的行为
同源策略控制了不同源之间的交互,但是并非限制了所有不同源的交互。通常允许以下行为:
- 通常允许跨域写操作,例如链接、重定向以及表单提交。特定少数的HTTP请求需要添加preflight。
- 通常允许跨域资源嵌入。例如利用script、link、img、iframe、video、audio等标签嵌入不同域的资源(可以设置资源是否可以被其他域访问)。这种内嵌资源的获取和访问是由浏览器来完成的,所以是安全的。此外,通过< script>标签等引入的JavaScript、CSS、图像等静态资源也被认为是与包含它们的界面所在的域同源。
- 通常不允许跨域读操作。但常可以通过内嵌资源进行读取访问,如调用内嵌脚本的方法(JSONP)。
防盗链
严格来说,同源策略用于JavaScript脚本,所以使用script、img等标签进行跨域资源嵌入是与同源策略无关的。那么别人也就可以在他们的页面上引用我们的服务器的图片了,但是如果我们不想给他们用呢?在请求头中,有个字段为referer,采用url的格式表示发送请求的网页或文件,我们可以在服务器代码中检查referer的值,来决定是否让这些网站使用。这就是防盗链的原理。
跨域
跨域利用一定的方法和技术,使JavaScript脚步可以访问获取原本不同源的服务器、窗口和文档、存储数据的内容。常用的方法有设置相同的域、跨域资源共享、JSONP、跨文档信息、WebSockets等。
变更源
同源限制的是不同源内容之间的访问,那么我们只需要将两个页面的源设成一致就可以相互访问了。
实现方法:通过JavaScript,将A页面和B页面的document.domain设置为相同的域名。
限制及适用范围:
- 同个网页的一级域名需要相同。如http://store.company.com/dir/other.html的域名可以设置为`document.domain = “company.com”`,但是不能设置为othercompany.com。另外赋值时,会以null覆盖原来的端口号,所以赋值时要带上端口号
- 此方法只适用于Cookie和窗口,LocalStorage和IndexDB无法通过此方法规避同源,而要使用PostMessage。
- 也可以在服务器设置Cookie时,指定Cookie的所属域名为一级域名,这样只有一级域名相同,就可以访问该Cookie。
CORS跨域资源共享
跨域资源共享是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同域访问其资源。而这种访问是同源策略所禁止的。它定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。跨域资源共享的基本思想,是通过自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是否应该成功。
默认情况下,XHR对象只能访问与包含它的页面位于同一域中的资源,所以通过xhr.open()打开的是相对路径的url,如果浏览器支持CORS,则可以将url设置为指向其他域资源的绝对路径的url。将请求发送给服务器后,服务器会根据这个头部信息(Origin,说明请求的源)决定是否给予响应,可以接受的话,就在Access-Control-Allow-Origin头部中回发相同的源信息(如果是公共资源,可以回*),如果没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。
与JSONP相比,CORS无疑更加先进:
- JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
- 使用CORS,开发者可以使用普通的xhr对象发起请求和获得数据,比起JSONP有更好的错误处理。
JSONP
JSONP是JSON with padding的简写,是应有JSON的一种方法。JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON数据。JSONP是通过动态< script>元素来使用的,使用时可以为src属性指定一个跨域URL。
总的来说,JSONP是一种在客户端定义函数,在服务器端传入数据并传递到客户端调用该函数的一种方式。所以JSONP要求服务器和客户端的密切配合,并且只能实现GET请求。且数据格式要为可以通过script标签传递的JSONP格式。
实例如下:
1 | //客户端 |
Web Sockets
Web Sockets的目标是在一个单独的持久连接上提供双工、双向通信。使用标准的HTTP服务器无法实现Web Sockets,只要支持这种协议的专门服务器才能正常工作。
1 | var socket=new WebSocket("ws://www.example.com/server.php"); |
window.postMessage
HTML5为解决文档间的通信问题,引入了一个全新的API,它为window对象新增了一个window.postMessage方法,允许跨域的窗口通信。
父窗口可以使用window.open()或iframe等打开一个不同源的子窗口,则父窗口和子窗口可以进行通信。示例如下:
1 | //父窗口http://a.com脚本 |
总结
同源策略是限制JavaScript脚本访问不同源的资源的一种安全措施,是web安全的基石。所以script、img等标签不受同源策略的限制(因为其资源的请求是浏览器发起的,是安全的),但是可以通过防盗链技术(检查请求头的referer)来限制我们的资源被其他网页的使用的权限。
同源策略限制JavaScript脚本访问不同源的存储数据(Cookie、LocalStorage、IndexDB)、窗口和文档(其绝大部分属性)以及服务器资源(XMLHttpRequest发起的请求要同源),以防止恶意的网址获取用户的信息。但是,实现合理的跨域请求也是必须的。
为了使同一个一级域名下网站间进行相互访问,可以将其子网址的域名通过document.domain统一设为一级域名,子网站间就不再受同源限制了。为了使Ajax发起不同源的请求,可以使用跨域资源共享方法,设置Orign和Access-Control-Allow-Origin头部来指定该服务器信任的网站,也可以使用JSONP,但它有很大的限制。此外,也可使用WebSocket来实现客户端与服务器端的通信,它不实行同源策略。为了不同网页间进行信息传输,则可以使用postMessage方法来实现。