设计API接口如何保证安全调用
接口的安全性
接口的安全性主要围绕Token、Timestamp和Sign三个机制展开设计,保证接口的数据不会被篡改和重复调用,下面具体来看:
Token授权机制
Token授权机制:用户使用用户名密码登录后服务器给客户端返回一个Token(通常是UUID),并将Token-UserId以键值对的形式存放在缓存服务器中。服务端接收到请求后进行Token验证,如果Token不存在,说明请求无效。
时间戳超时机制
时间戳超时机制:用户每次请求都带上当前时间的时间戳timestamp,服务端接收到timestamp后跟当前时间进行比对,如果时间差大于一定时间(比如5分钟),则认为该请求失效,这个时间要保证足够完成本次请求的同时尽量短,可以减少缓存服务器的压力(见签名机制)。
签名机制
签名机制:将Token和时间戳加上其他请求参数就行MD5或SHA-1算法(可根据情况加点盐)加密,加密后的数据为本次请求的签名sign,并将该签名存放到缓存服务器中,超时时间设定为跟时间戳的超时时间一致(这就是为什么要尽量短,二者时间一致可以保证无论在timestamp规定时间内还是外本URL都只能访问一次)。服务端接收到请求后以同样的算法得到签名,并跟当前的签名进行比对,如果不一样,说明参数被更改过,直接返回错误标识。同一个签名只能使用一次,如果发现缓存服务器中已经存在了本次签名,则拒绝服务。
整个流程如下:
-
1、客户端通过用户名密码登录服务器并获取Token
-
2、客户端生成时间戳timestamp,并将timestamp作为其中一个参数
-
3、客户端将所有的参数,包括Token和timestamp按照自己的算法进行排序加密得到签名sign
-
4、将token、timestamp和sign作为请求时必须携带的参数加在每个请求的URL后边(http://url/request?token=123×tamp=123&sign=123123123)
-
5、服务端写一个过滤器对token、timestamp和sign进行验证,只有三个参数都正确且在规定时间内,本次请求才有效
在以上三中机制的保护下,
如果黑客劫持了请求,并对请求中的参数进行了修改,签名就无法通过;
如果黑客使用已经劫持的URL进行DOS攻击,服务器则会因为缓存服务器中已经存在签名而拒绝服务,所以DOS攻击也是不可能的;
如果黑客隔一段时间进行一次DOS攻击(假如这个时间大于签名在缓存服务器中的缓存时长),则会因为时间戳超时而无法完成请求,这就是为什么签名的缓存时长要跟时间戳的超时时长一样。
API接口设计 注意问题
跨平台性
所谓跨平台是指我们的接口要能够支持不同的终端,比如Android、iOS、windowsphone以及桌面软件、网站等。如:不同的终端每页显示的记录数不同
-
采用通用的解决方案,比如通信协议就采用最常用的HTTP协议,如果是即时通信,可以采用开放的XMPP协议,做游戏的可以采用可靠的TCP协议,除非TCP不够用了,再采用定制的UDP协议。
-
数据交换采用xml或者json格式或者webservice等等。总之,要达到的目标就是让不同的端能够很方便的使用你的接口。
良好的响应速度
-
接口应该以最快的速度将数据返回给请求者,要达到的目标就是快,一个页面,秒开最好,超过三秒就需要找找原因了。
-
数据量按需分配,APP客户端需要什么数据就返回什么数据,过多的数据量影响处理速度,最重要的是影响传输效率
接口要为移动客户端考虑
比如,在移动端里,下拉刷新和上拉加载更多是很常见的功能,如果接口仍然按照传统的web思路, 只提供按页读取的话,就会造成移动端的额外的数据请求和计算。 这时,接口就应该针对这两种类型的操作提供额外的支持。
考虑移动端的网络情况和耗电量
如果让我们说出哪类app比较好,可能还不大好说,但是如果让我们说出哪些app很差,我们肯定会说出那些体积很大、占用内存多、界面很卡、费电的app 不好。
对于网络情况,接口应该具备为不同的网络提供不同的内容的能力如果我们能够知道用户的网络情况,只有在wifi的情况下才给用户传输封面图、缩略图 之类的,是不是可以帮用户节省很多流量呢
通用的数据交换格式
-
目前,对于接口和客户端的数据交换格式,基本上就是三种,xml和json和webservice,而现在使用json的应该占大多数。
-
最麻烦的就是处理Date类型,因为JSON本身没有Date类型,因此,JSON库将Date类型的数据序列化时会转为String。这时,不同环境, 不同平台,以及用不同的JSON解析库,转换后的结果经常会不同。比如,你在开发机上可能得到的结果是”2016-1-1 17:11:11”,但放到服务器后结果却变成了“Jan 1,2016 5:11:11 PM” ,客户端进行反序列化时无疑会失败。后来,我取消了所有Date类型,统一采用时间戳表示,就再没有转化的烦恼了。
-
另外,接口的开发人员有时候会将一些数据错误地转换为了String,导致客户端使用时因类型错误而异常。例如,本来是数字的1,被转成 了”1”,客户端做运算时就会出错,或用switch判断时也会出错,或其他无法转换的情况发生时;
-
例如,为空时JSON正确地表示应该是null,但如 果转为了String就变成了”null”,那问题就来了,我遇到的因为这个错误的转换导致的程序奔溃已经好几次了,第一次的时候,查了一整天才定位到问题所在
接口统计功能
在做PC端网站的时候,我们都会给我们的网站加上个统计功能,要么自己写统计系统,要么使用第三方的比如GA
移动端接口API则需要我们自己实现统计功能,这时就需要我们尽可能多的收集客户端的信息,除了传统的IP、User-Agent之外,还应该收集一些移动 相关的信息,比如手机操作系统,是android还是ios,都是什么版本,用户使用的网络状况,是2G、3G、4G还是WIFI。客户端APP是什么版 本信息。
客户端与服务端的肥瘦平衡
在移动开发中,由于客户端的修改会很费时费力,特别是IOS应用还要经过Apple审核,另外,当前IOS开发人员、Android开发人员的人工成本普遍较高,人才紧缺,基于这两点,
能在服务器端实现 的功能就不要放在客户端,毕竟服务器端程序的修改要比客户端方便、灵活、快捷的多。
隐式用户与显式用户
显式用户指的是,APP程序中有用户系统,一个username、password正确的合法用户,称之为显式的用户,
通常显式用户都需要注册,登录以后能完成一些个人相关的操作。 隐式用户指的是,APP程序本身就没有用户系统,或者一个在没有登录的情况下,使用我们APP的用户。
在这种情况下,可以通过客户端生成的UDID来标识一个用户。
有了用户信息,我们就能够了解不同用户的使用习惯,而不仅仅是全体用户的一个整体的统计信息,
有了这些个体的信息之后,就可以做一些用户分群、个性化推荐之类的事情。
安全问题
设计API第一个需要考虑的是API的安全机制。我负责的上一个项目,因为API的安全问题,就被人攻击了两次。之后经过分析,主要存在两个漏洞:
- 一是因 为缺少对调用者进行安全验证的方式,
二是因为数据传输不够安全。
制定API的安全机制
主要就是为了解决这两个问题:
- 保证API的调用者是经过自己授权的App;
- 保证数据传输的安全。
解决方案
第一个问题的解决方案
主要采用设计签名的方式。
-
对每个客户端分别分配一个AppKey和AppSecret。需要调用API时,将AppKey加入请求参数列表,并将AppSecret和所有参数一起,根据某种签名算法生成一个签名字符串,然后调用API时把该签名字符串也一起带上。
-
服务端收到请求之后,根据请求中的AppKey查询相应的AppSecret,按照同样的签名算法,也生成一个签名字符串,当服务端生成的签名和请求带过来的签名一致的时候,那就表示这个请求的调用者是经过自己授权的,证明这个请求是安全的。
-
而且,每个端都有一个Key,也方便不同端的标识和统计。为了防止AppSecret被别人获取,这个AppSecret一般写死在代码里面。
-
另外,签名算法也需要有一定的复杂度,不能轻易被别人破解,最好是采用自己规 定的一套签名算法,而不是采用外部公开的签名算法。
-
另外,在参数列表中再加入一个时间戳,还可以防止部分重放攻击。
第二个问题的解决方案
主要就是采用 HTTPS了。
-
HTTPS因为添加了SSL安全协议,自动对请求数据进行了压缩加密,在一定程序可以防止监听、防止劫持、防止重发,主要就是防止中间人攻击。
-
因此,为了安全考虑,建议对SSL证书进行强校验,包括签名CA是否合法、域名是否匹配、是不是自签名证书、证书是否过期等。
-
接口不能直接调用OAuth认证(rsa加密),ip白名单接口的安全工作不能马虎,暴力破解啊、SQL Injection啊、伪造请求和数据啊、重复提交啊也要考虑到,
-
如果数据特别敏感,可以考虑采用SSL/TLS等加密传输,或者客户端、服务器端约定一个加密算法和密钥,对来往传输的数据进行加密、解密。如将所有参数加签名算法得到一个签名验证参数signhttp://hudeyong926.iteye.com/blog/2287954
-
表单类接口防止重复提交:调用过的接口sign存起来,检查sign是否存在
良好的接口说明文档和测试程序
-
接口文档要清晰、明了,包含多少个接口,每个接口的地址、参数、请求方式、数据交换格式、参数是否必填、编码格式UTF8,返回值等都要写清楚。
-
接口测试程序,有条件的话,也可以提供,方便前后端的调试
版本的维护
随着业务的变化,客户端APP和服务器端API都会发生变化,增加新的功能,修改已有的功能, 增加功能还好说, 如果是接口需要修改,那么就面临着同一个接口要同时为不同版本的客户端服务的问题。 因此,服务器端接口也要做好相应的版本维护。
-
主版本更新可以把版本号放入API的URL中/api-v2来指出所使用的API版本
-
次要版本的修改是通过客户在API调用时发起请求的HTTP头部做指定的头部的版本元素看起来是这样的: Element-Version: 1
接口数据、状态
接口必须提供明确的数据状态信息,不管是成功的,还是失败的,都必须返回给APP客户端。
接口、参数命名准确。
无论是接口还是参数,命名都应该有意义,让人一目了然。接口调试技巧前提必须放在外网上
-
服务端return 调试信息,客户端调用并显示结果,
-
在服务端将结果保存成文件在打开文件查看,即日志型调试(或建临时表放在数据库表里)
-
考虑突然断网或接口信息返回超时异常情况的业务处理(先扣金额更新状态,如有问题自动返回)