1 JSONP的原理与实现
1.1 同源策略
前端跨域是每个前端人绕不过的坎,也是必须了解的一个知识点。我记得第一次遇到前端跨域这个坑的时候,真是无语到极点,对于一个移动端出身的人来说,觉得这个玩意无可理喻。但是后来慢慢了解,觉得前端的同源策略是非常有必要的。同源策略就是浏览器默认让www.baidu.com
不能加载来自www.google.com
的数据。对于现在来说,所有数据都是同源的可能性基本上很小,比如我们公司静态资源www.image.com
和前端资源www.htmlcss.com
的CDN路径都不一样,前端获取后台数据www.apidata.com
又是另一个地址。如何解决这个坑呢?我们公司通过两种方式来避开。具体就是通过设置Access-Control-Allow-Origin
来做POST
请求,用JSONP
来实现GET
请求,因为JSONP
只能实现GET
请求。
1.1.1 通过Access-Control-Allow-Origin支持跨域
有些人肯定就纳闷了,我就喜欢跨域,我就不关注安全,难道就没有办法了吗?当然是否定的。你需要做的,只是让服务器在返回的header里面加上Access-Control-Allow-Origin
这个域就可以了。这样浏览器在接收到服务器返回的数据,就不会因为违反同源策略限制你拿到数据了。下面就用抓包来具体看一下:
当我打开这里点开h5链接这个链接的时候。会去https//m.ctrip.com
通过POST
请求数据,这里就用到了跨域。
1 | :method: POST |
服务器返回的响应头如下:
1 | :status: 200 |
我们可以看到,这里有access-control-allow-origin
这个响应域就解决了问题。这个方法是最简单的,而且前端POST
请求最常见的方法(不确定还有其他好的解决方案)。这种方式最好就是通过他获取服务数据,不要加载js脚本。小心被别人注入攻击。
1.1.2 JSONP的基本原理
讲JSONP
之前,我先亮出一段常见的代码。下面这个方法主要就是动态的创建一个script
标签,然后设置src属性。并且添加到document
的第一个script
标签之前。也就是说动态去加载一个javscript
脚本。
1 | function loadJs(src, attrs = {}) { |
最有意思的是script
标签的src
不受跨域限制。也就是说wwww.baidu.com
的文件可以通过上面这个方法无限制的加载www.google.com
的js文件。这个就是JSONP
的实现的最基本原理。每一个JSONP
请求就是动态的创建script
元素,然后通过src属性去加载数据,而且一般是通过callback这个回调方法来返回服务器数据,然后再把script
标签移除。如此周而复始的循环,想想都累啊。下面看一个JSON的标准格式,服务器会获取到callback
这个回调方法。然后通过方法调用
的方式把数据返回来,也就是执行callbackFun
方法。serverdata
就是服务器给客户端的数据。至于callback
这个名字,可以自己定义,有客户端和服务器商量决定。
1 | function callbackFun(serverdata){ |
1.2 JSONP的实现
下面我会对JSONP
做一个最基本的实现。使用Vue
和node.js
分别实现客户端和服务端,代码地址。
首先我们先看客户端的实现:
1 | //获取header的第一个子元素 |
这段代码主要做了如下几件事:
- 创建一个
script
标签元素,并且添加到header
元素里面。 - 拼接
script
元素的src
属性,其中必然好汉callback这个参数,服务端根据这个参数的值回调。 - 回调以后需要手动把
script
标签元素移除,并且删除全局的回调函数名。
客户端的使用如下,是不是感觉简洁明了,比ES5的回调爽多了:
1 | import jsonpRequest from "../lib/jsonpRequest.js"; |
服务端的实现如下。
1 | let express = require('express'); |
服务端代码说明如下:
res.end
是express
表示对http请求返回。具体返回的数据类似于callback随机数(服务端数据)
这种类型。- 客户端在收到
callback随机数(服务端数据)
这个数据以后,会自动按照javascript脚本解析执行。具体就是一个全局方法调用,方法名是callback随机数
,参数是服务端数据
。这样就实现了服务端数据的回调。 - 客户端在global对象下面注册了
callback随机数
这个方法。具体代码是上面global[fnName] = function(ret) {
这一行。 callback随机数
是服务端和客户端商量,具体可以自己决定,真实的时候类似于callbacksuijishu
这种类型。
1.2.1 JSONP请求报文
JSONP
本质上就是一个普通的GET
请求。无非就是这个请求是通过script
标签来发送的。而且请求参数里面必定会有一个callback
参数。
下面我们具体抓包看一下我们的请求报文:
1 | GET /jsonp/jsonpRequest?name=%E8%80%81%E9%BB%84&site=www.huangchengdu.com&callback=jsonpiFuL4 HTTP/1.1 |
返回报文:
1 | HTTP/1.1 200 OK |
从上面的报文我们可以返现。请求的callback
参数的值和返回的响应体的名称是一样的。响应提就是一个普通的函数。服务器返回的数据作为函数的参数。
2 XSS攻击
XSS
的全称是Cross-site scripting
,翻译过来就是跨站脚本
。script
可以跨域加载脚本这个特性,合理利用比如JSONP
。如果不合理利用,比如某个坏人通过某种方式,让你的浏览器去加载恶意的javascrpt
脚本,必然就会导致敏感信息被盗或者财务损失。最常见的就是XSS
攻击,其实就是注入恶意脚本。真是凡事都有利有弊,就看如何使用了。常用的XSS攻击手段和目的有如下几种:
- 盗用cookie,获取敏感信息。
- 利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。
- 利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、- 发私信等操作。
- 利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。
- 在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果。
如果某一个字符串里面有var a = 1;<script>alert('我是你大爷')</script>;var b = 2;
这种类型的字符串。而且我们刚好要通过script
标签加载。那么他就会弹出一个我是你大爷
。避免的方式就是把存在这种可能性的地方都处理过,如果包含类似<script>
这种字符的脚本就处理掉或者干脆返回错误。目前最常见的预防操作有如下几种:
- 将重要的cookie标记为http only,这样的话Javascript 中的document.cookie语句就不能获取到cookie了。
- 只允许用户输入我们期望的数据。例如:年龄的textbox中,只允许用户输入数字。 而数字之外的字符都过滤掉。
- 对数据进行Html Encode处理。
- 过滤或移除特殊的Html标签,例如:”script,iframe,for等”。
- 过滤JavaScript事件的标签。例如”onclick=”,”onfocus”等等。
3 CSRF攻击
这玩意我了解不多,也无法做出模拟操作。跨站请求伪造(英语:Cross-site request forgery),也被称为one-click attack或者session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。[1] 跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
我的理解就是,比如你刚去淘宝买了东西,并且浏览器有你的session
护着cookie
之类的信息。然后你马上又进入一个不该去的网站,并且点击了里面的一个淘宝链接,然后在你不知情的情况下做一些违法操作。这样阿里后台是不知道的,因为你刚刚通过合法手段买了东西,从而达到在你不知情的情况下,而且淘宝也信任你的情况下,畏畏缩缩偷偷摸摸的干坏事。
3.1 SCRF预防
检查Referer字段,通过这个字段来判断用户是从那个地址跳转到当前地址的。HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。
添加校验token,这个就最常见了,现在那个前端网站还不加一个验证码啊。不管你如何千变万化,你验证码中是用户数据的吧,而且现在好像越来越流行手机号码验证了。CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验token的值为空或者错误,拒绝这个可疑请求。
...
...