REST API安全认证研究
2017年09月27日


一、概述


    对外网暴露的RESTful API,由于是无状态的,如果不做认证,那就相当于裸奔的,任何人都可以调用,随意调用,这样是极不安全的。下面就RESTful API的安全性方案进行了一些研究。

  (但是首先建议,核心系统的API不对外网暴露,只允许内网调用,而且不建议做成HTTP RESTful形式。如果非要使用RESTful API对外网暴露接口,那么请看下面)。


RESTful API的安全性,包括了如下三个方面: 

a) 对客户端做身份认证 

b) 各种安全防护措施 

c) 身份认证之后的授权 


1、对客户端做身份认证

面向最终用户的API,采用HTTPS + OAUTH 2的方式来认证比较好。

面向服务器的API,则比较简单,方式比较多。

2、各种安全防护措施

包括:防止敏感数据泄露,防篡改,防重放攻击,防DDoS攻击。应对方案包括:

采用HTTPS;DDoS流量清洗;对敏感数据部分加盐再加密传输;在请求中增加一次性的Token。

3、身份认证之后的授权,由应用服务器端根据业务需求自己实现,比如安全框架Shiro、SpringSecurity等。


参考资料

http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html

http://open.weibo.com/wiki/index.php/Weibo-JS_V2

https://developer.github.com/v3/


考虑两种黑客攻击情况

1、针对HTTP的网络劫持;

2、针对HTTPS的中间人攻击+SSLstrip


防御措施,要点如下:

1、全站采用HTTPS

2、客户端、WEB端的数据单向加密传输


采用全站HTTPS后,能避免绝大多数的攻击。而针对HTTPS的攻击,难度很大(目前市面上对HTTPS成功的攻击还是比较少见),如果不是非常非常核心的数据,是可以不考虑特殊保护的。


针对HTTPS的进一步保护手段,是采用数据加密,对客户端和WEB端用户发送的数据,进行加密再传输,即使被黑客获取了,也无法篡改和利用。


二、客户端身份认证方案


1、客户端会话认证

    两种大的方案:

a)基于服务器端(分布式)Session技术

b)基于JWT(Json Web Token)技术

通常来说,针对Web 浏览器端的应用,基本用(a)方案,而手机APP端的应用,一般用(b)方案。


分布式Session技术

基本上都是用Redis来存储Session信息。Session的方案比较常规,不做过多说明。


JWT技术(基于token的鉴权机制)

参见《JWT技术-基于token的鉴权机制》一文。


2、OAUTH 2.0认证


。。。。。。。。。。。。




三、关于HTTPS的安全性问题

首先来说,从目前全世界的报道来看,HTTPS数据加密应该是足够安全的,黑客很难破解。

但是,对HTTPS的应用,仍然可以变相的攻击:重放攻击。


首先,模拟一个重放攻击的例子,如下:

常规登录流程:

1、前端web页面用户输入账号、密码,点击登录。

2、请求提交之前,web端首先通过客户端脚本如javascript对密码原文进行md5加密。

3、提交账号、md5之后的密码

4、请求提交至后端,验证账号与密码是否与数据库中的一致,一致则认为登录成功,反之失败。


但是,如果监听者截取到了登录信息:

http://****/login.do?method=login&password=md5&userid=登录账号

把它重放一下,即可冒充你的身份登录系统。


解决思路如下:

1、把这个url请求做成一次性使用的,第二次(重放)请求就无效了。

2、每次请求传输一个token值,第二次如果还是一样的token,就无效。这个token的生成算法或者密码数据是保密的,监听者无法得知算法或者密码,从而无法仿制token。


方案一

1、如果是客户端APP,可以采用某种算法和密匙,将密码数据、时间截、随机数等加在一起生成token,然后再发送给服务器端验证,服务器验证通过后会记录下这个token,保存一段时间(比如24小时)。如果网络监听者再次发送一样的请求和token,则服务器端直接拒绝。由于网络监听者是不知道token生成算法或密匙的,如果他随意修改了token,服务器端根据密码数据、时间截、随机数等计算出来的token,就和接收到的token不一致,就拒绝这次访问。


举个实际例子:

    假设第一次请求数据:

DATA: {password:"ds8fds7", score: 10}

RS-TOKEN: AAAAAAAAAA (timestamp + nonce)

    如果黑客原封不动将这个请求再发一遍,显然这个token是会被拒绝的。所以要重新生成token,但是黑客不知道


方案二

2、也可以在调用这个API之前,先向服务器获取一个随机码(称之为盐值),在客户端和服务器端各保存一份,并且设置一个有效时间,客户端提交请求时,将md5之后的密码数据与该随机码拼接后,再次执行md5,然后提交(提交的token=md5(md5(密码数据)+随机码)),后端也会计算token,然后对比,对比成功则删除从服务器端这个随机码,监听者无法再次使用它进行登录。网络监听者即使再次提交这个请求,但是后端已经删除了随机码,所以无法通过。(这个方案的劣势在于,每次请求之前,都要先获取一个随机码,第二次请求时要携带上这个随机码)


3、针对方案一,如果不是客户端APP,而是WEB浏览器,算法和密匙是写在JavaScript里面的,怎么保证算法和密匙不被泄露?js是可以被拿到的,js函数可以被黑客执行,他可以调用js函数向服务器端发起请求。为避免这种情况,应该让网络监听者无法拿到可用的js,即使拿到了,也要限制js的执行,具体方案还有待研究,我考虑到两点,1是JavaScript混淆压缩,让js很难被反编译识别(目前来说是可以做到的),2是只允许js在指定环境下才能执行,例如有指定cookie或浏览器内存数据时才能执行,而cookie数据或内存数据需要从后端获取,js动态生成,且当该js生成的时候就携带一个标识,当js初始化时,就执行一次后端验证,验证通过该js可以执行,否则这个js不能执行。