前段时间,公司对运行的系统进行了一次安全扫描,使用的工具是 IBM 公司提供的 AppScan 。javascript
这个正所谓不扫没关系,一扫吓一跳,结果就扫出来这么个问题。前端
咱们的一个年老失修的内部系统,在登陆的时候,被扫描出来安全隐患,具体学名是啥记不清了,大体就是咱们在发送登陆请求的时候,有个字段名是 password
, AppScan 认为这个是不安全的,大概就是下面:java
我第一个反应是把这个字段名字改一下,毕竟能简单解决就简单解决嘛,结果固然是啪啪啪打脸。算法
这个名字我无论是换成 aaa
仍是 bbb
,再次扫描都还会报一样的问题,惟一不一样的地方就是安全报告上的字段名换一换。shell
这个就有意思了,这个问题是来者不善啊,通过我一翻查找(别问我怎么查的,问就是瞎猜的),找到缘由的所在了。数据库
由于咱们这个系统是一个内部系统,当时作登陆这我的比较图懒,就在页面上简单的作了个 form 表单提交,就好比这样:json
这个代码我曾经在大学的大做业上这么写过,没想到时隔多年我居然又见到了这样的代码,居然让我有一种老乡见老乡的特殊情感。浏览器
这个问题具体的缘由是 AppScan 是直接检测页面上 type='password'
的输入框,而后再检查请求中是否有对应的字段,别问我咋知道的,由于我干过把这里改为 type='text'
就不报错了,惟一的缺点就是页面上的密码框将会明码显示密码。安全
虽然我能够经过 js 来把输入框里的值动态的替换成任何我想要的样子,好比点啊、星号啊以及一些其余的样式,可是这么干总归有点不道德。服务器
问题找到了,那么怎么改呢?
到这里就涉及到了我今天要聊的内容了,如何保障 API 接口的安全性?
首先这个问题咱们分红两个部分来看,客户端和服务端。
由于我自己是作服务端开发的,这个问题固然要从服务端聊起。
我的以为安全措施主要体如今两个方面,一个是如何保证数据在传输过程当中的安全性,另外一个是如何在数据已经到达服务端后,服务端如何识别数据,保证不被攻击。
下面咱们一条一条来聊:
HTTP 请求中的来源识别就是,服务端如何识别当前的请求是由本身的客户端发起的,而不是由第三方模拟的请求。
咱们先看下一个正常的 HTTP 请求的头里面会有什么内容:
我打开百度的首页,经过 network 随便抓了一个请求,查看这个请求的请求头,这里面我要说的几个字段都用红框框起来了:
咱们通常会对 HTTP 请求头中的 Origin 和 Referer 作白名单域名校验,先判断这个请求是否是由咱们本身的域发出来的,而后再对 User-Agent 作一次校验,用来保证当前的请求是由浏览器发出来的,而不是由什么杂七杂八的模拟器发出来的。
当前,因为前端是彻底不可被信任的,上面这几个字段都是能够被篡改和模拟的(我在前面写爬虫的文章中绝对写过),可是,能作的校验尽可能作,咱们不能一次把全部的漏洞都堵上,可是至少能堵上一部分。
数据在传输过程当中是很容易被抓包的,若是直接传输好比经过 http 协议,那么用户传输的数据能够被任何人获取;因此必须对数据加密。
常见的作法对关键字段加密好比用户密码直接经过 md5 加密;如今主流的作法是使用 https 协议,在 http 和 tcp 之间添加一层加密层( SSL 层),这一层负责数据的加密和解密。
增长签名就是咱们在发送 HTTP 请求的时候,增长一个没法伪造的字符串,用来保证数据在传输的过程当中不被篡改。
数据签名使用比较多的算法是 MD5 算法,这个算法是将要提交的数据,经过某种方式组合成一个字符串,而后经过 MD5 算法生成一个签名。
我用前面那个登陆的接口举个简单的例子:
srt:name={参数1}&password={参数2}&$key={用户密钥} MD5.encrypt(str)
这里的 key 是一个密钥,由客户端和服务端各持有一份,最终登陆请求要提交的 json 数据就会是下面这个样子:
{ "name": "test", "password": "123", "sign": "098f6bcd4621d373cade4e832627b4f6" }
密钥是不参与数据提交,不然请求被劫持后,第三方就能够经过密钥本身生成签名,固然,若是以为单纯的 MD5 不够安全的话,还能够在 MD5 的时候加盐和加 hash ,进一步下降请求被劫持后存在模拟的风险。
时间戳机制主要用来应对非法的 DDOS 攻击,咱们的请求通过的加密和签名后,已经很难进行逆向破解了,可是有的攻击者他在抓包后,并不在乎里面的具体数据,直接拿着抓的包进行攻击,这就是臭名昭著的 DDOS 攻击。
咱们能够在参数中加上当前请求的时间戳,服务端拿到这个请求后会拿当前的时间和请求中的时间作比较,好比在 5 分钟以内的才会流转到后面的业务处理,在 5 分钟之外的直接返回错误码。
这里要注意的是客户端的时间和服务端的时间基本上是不可能一致的,加上请求本省在网络中传输还有耗时,因此时间限制的阀值不能设定的过小,防止合法的请求没法访问。
我仍是拿上面的登陆举例子,到这一步,咱们的请求数据会变成下面这个样子:
{ "name": "test", "password": "123", "timestamp": 1590334946000, "sign": "098f6bcd4621d373cade4e832627b4f6" }
不少时候,咱们一个 API 接口可能并非只会有一个客户端进行调用,可能调用方会有很是多,咱们的服务端为了验证合法的调用用户,能够添加一个 AppID 。
想要调用咱们的 API 接口,必须经过线下的方式像我申请一个 AppID ,只有当这个 AppID 开通后,才能对个人接口进行合法的访问,在进行接口访问的时候,这个 AppID 须要添加到请求参数中,与其余数据一块儿提交。
到了这一步,咱们上面那个登陆接口的传入参数就变成了下面这样:
{ "appid": "geekdigging", "name": "test", "password": "123", "timestamp": 1590334946, "sign": "098f6bcd4621d373cade4e832627b4f6" }
咱们上面对请求的参数进行了一系列的处理,整体思想是防止第三方进行抓包和破解,可是若是我不是第三方呢,好比我就在浏览器的 network 中进行抓包,这个请求中的数据我就能看的清清楚楚明明白白,攻击者能够先经过正规的途径进行访问,当分析清楚咱们的套路后再对请求进行伪造,开始攻击,这时咱们前面的努力好像就都白费了。
不要说什么没有人会这么作,我就举一个例子,支付宝网页端的登陆接口,若是能搞清楚其中请求的发送规则,攻击者就可使用买来的用户数据库,进行批量撞库测试,经过请求响应的结果就能够验证一批的支付宝的帐号密码(固然不会有这么简单哈,我只是举例子)。
咱们接下来还能再对请求作一次总体加密,如今主流的加密方式有对称加密和不对称加密两种。
对称加密:对称密钥在加密和解密的过程当中使用的密钥是相同的,常见的对称加密算法有 DES , AES , RC4 , Rabbit , TripleDes 等等。优势是计算速度快,缺点是在数据传送前,发送方和接收方必须商定好秘钥,而后使双方都能保存好秘钥,若是一方的秘钥被泄露,那么加密信息也就不安全了。
不对称加密:服务端会生成一对密钥,私钥存放在服务器端,公钥能够发布给任何人使用。优势就是比起对称加密更加安全,可是加解密的速度比对称加密慢太多了。普遍使用的是 RSA 算法。
对上面咱们提交的数据作一次 DES 加密,密钥使用 123456 ,咱们能够获得这样一个结果:
U2FsdGVkX18D+FiHsounFbttTFV8EToywxEHZcAEPkQpfwJqaMC5ssOZvf3JJQdB /b6M/zSJdAwNg6Jr8NGUGuaSyJrJx7G4KXlGBaIXIbkTn2RT2GL4NPrd8oPJDCMk y0yktsIWxVQP2hHbIckweEAdzRlcHvDn/0qa7zr0e1NfqY5IDDxWlSUKdwIbVC0o mIaD/dpTBm0=
而后咱们把这个字符串放到请求中进行请求,咱们刚才的登陆请求就会变成这样:
相信这样一来,超过 99% 的攻击者看到 network 中这样的抓包请求都会放弃,可是还剩下 1% 的人会打开 Chrome 提供的开发者工具进行一行一行的 debug ,针对这部分人,咱们下面在客户端的段落里再聊如何对付他们。
不少时候,在某些并发比较高的场景下,基于对业务系统的保护,咱们须要对请求访问速率进行限制,防止访问速率太高,把业务系统撑爆掉。
尤为是一些对外的接口,给客户或者供应商使用的接口,由于调用方咱们本身没法控制,天知道对方的代码会怎么写。
我曾经见过供应商把咱们提供的修改数据的接口拿来当作批量接口跑批,天天晚上都能把那个服务跑挂掉,后来直到咱们去问,供应商他们才说天天晚上会用这个接口作上千万的数据同步,我也是醉了。
出于安全的角度考虑,在服务端作限流就显得十分有必要。
服务端限流的算法常见的有这么几种:令牌桶限流、漏桶限流、计数器限流。
令牌桶限流:令牌桶算法的原理是系统以必定速率向桶中放入令牌,填满了就丢弃令牌;请求来时会先从桶中取出令牌,若是能取到令牌,则能够继续完成请求,不然等待或者拒绝服务;令牌桶容许必定程度突发流量,只要有令牌就能够处理,支持一次拿多个令牌。
漏桶限流:漏桶算法的原理是按照固定常量速率流出请求,流入请求速率任意,当请求数超过桶的容量时,新的请求等待或者拒绝服务;能够看出漏桶算法能够强制限制数据的传输速度。
计数器限流:计数器是一种比较简单粗暴的算法,主要用来限制总并发数,好比数据库链接池、线程池、秒杀的并发数;计数器限流只要必定时间内的总请求数超过设定的阀值则进行限流。
实现方面来说, Guava 提供了 RateLimiter 工具类是基于基于令牌桶算法,有须要的同窗能够本身度娘一下。
黑名单机制已经有点风控的概念了,咱们能够对非法操做进行定义。
好比记录每一个 AppID 的访问频次,若是在 30 分钟内,发生了 5 次或者以上的超频访问而且超出了 10 倍以上的访问量,这时能够将这个 AppID 放入黑名单中, 24 小时之后或者调用方线下联系才能将这个 AppID 取出。
再好比记录 AppID 超时访问次数,正常来说,超时访问不会频繁发生,若是在某个时间段内,大量的出现超时访问,这个 AppID 必定存在问题,也能够将其先放入黑名单中让它冷静冷静。
黑名单实际上更多的是应用在业务层面,好比你们可能碰到过的拼爹爹的风控,直接把帐户扔到黑名单里面,禁止这个帐户对某些补贴商品的下单。
在当今的互联网时代,网页和 APP 成为了主流的信息载体。
其中 APP 是可使用一些加固技术对 APP 进行加固,防止别人进行暴力破解。
而网页就比较困难了,网页的动态都是依靠 JavaScript 来完成的,逻辑是依赖于 JavaScript 来实现的,而 JavaScript 又有下面的特色:
基于这两点,致使了 JavaScript 代码是不安全的,任何人均可以读取、分析、盗用、篡改 JavaScript 代码。
因此说, JavaScript 若是不进行一些处理,无论使用了如何高超的加解密方案,在被人找到其中的逻辑后,被模拟或者复制将变得在所不免。
前端 JavaScript 常见的加固方案有这么几种:压缩、混淆、加密 。
代码压缩,就是去除 JavaScript 代码中没必要要的空格、换行等内容,把一些可能公用的代码进行处理实现共享,最后输出的结果都压缩为一行或者几行内容,代码可读性变得不好,同时也能提升网站加载速度,就想下面这样:
这个是我从百度的页面上随便找了一个 js 截出来。
若是是单纯从去除空行空格这个角度上来对代码进行压缩,其实几乎是没有任何防御做用的,由于这种压缩方式仅仅是下降了代码的直接可读性。
咱们能够经过各类工具对代码进行格式化,包括 Chrome 浏览器自己就提供了这个功能。
目前主流的前端技术都会使用 Webpack 进行打包,Webpack 会对源代码进行编译和压缩,输出几个打包好的 JavaScript 文件,其中咱们能够看到输出的 JavaScript 文件名带有一些不规则字符串,同时文件内容可能只有几行内容,变量名都是一些简单字母表示。
这其中就包含 JavaScript 压缩技术,好比一些公共的库输出成 bundle 文件,一些调用逻辑压缩和转义成几行代码,这些都属于 JavaScript 压缩。另外其中也包含了一些很基础的 JavaScript 混淆技术,好比把变量名、方法名替换成一些简单字符,下降代码可读性。
总体上来说, JavaScript 压缩术只能在很小的程度上起到防御做用,要想真正提升防御效果还得依靠 JavaScript 混淆和加密技术。
JavaScript 混淆是彻底是在 JavaScript 上面进行的处理,它的目的就是使得 JavaScript 变得难以阅读和分析,大大下降代码可读性,是一种很实用的 JavaScript 保护方案。
JavaScript 混淆器大体有两种:
第一种实现成本低,可是效果也通常,适合对混淆要求不高的场景。第二种实现成本较高,可是更灵活,并且更安全,更适合对抗场景。
经过语法树替换实现的混淆器,这种混淆方式的实现有点复杂了,我这里就不展开去聊了,有兴趣的同窗能够参考这篇文章:https://www.zhihu.com/question/47047191/answer/121013968 。
针对修改语法树进行混淆的方式,目前有一家作的比较好而且提供商业服务的是 jscrambler ,他们的官网地址:https://jscrambler.com/ 。
总之,以上方案都是 JavaScript 混淆的实现方式,能够在不一样程度上保护 JavaScript 代码。
在通常的场景中,第一种混淆方式足够咱们使用,如今 JavaScript 混淆主流的实现是 javascript-obfuscator 这个库,利用它咱们能够很是方便地实现页面的混淆,它与 Webpack 结合起来,最终能够输出压缩和混淆后的 JavaScript 代码,使得可读性大大下降,难以逆向。
不一样于 JavaScript 混淆技术,JavaScript 加密技术能够说是对 JavaScript 混淆技术防御的进一步升级,其基本思路是将一些核心逻辑使用诸如 C/C++ 语言来编写,并经过 JavaScript 调用执行,从而起到二进制级别的防御做用。
其加密的方式如今有 Emscripten 和 WebAssembly 等,其中后者愈来愈成为主流。
感兴趣的同窗能够自行度娘了解下。
上面介绍了这么多,只是为了咱们的程序可以更加安全稳定的运行,减小由于攻击而产生的损失(加班)。
不少内容都是来自度娘后进行信息整理,但愿各位可以善用搜索引擎这个工具。