美文网首页
RESTful api接口安全优雅设计

RESTful api接口安全优雅设计

作者: 狂野小青年_9c9f | 来源:发表于2018-01-12 15:07 被阅读0次

    RESTful api接口安全优雅设计                                                                    

                                                                                                                                                                                                                                                    -- 陈万洲


    写过不少接口,不过一直没有去总结,网上搜了一下,大同小异,此文根据以下几个链接整理修改:

    https://segmentfault.com/a/1190000004051246

    http://blog.sqrtthree.com/2015/09/08/api/

    http://keeganlee.me/post/architecture/20160107

    https://www.hutuseng.com/article/how-to-design-api

    在项目中,需要为APP撰写API。刚开始接触的时候,并没有考虑太多,就想提供URL,APP端通过该URL进行查询、创建、更新等操作即可。但再对相关规范进行了解后,才发现,API的设计并没有那么简单,远远不是URL的问题,而是一个通信协议的整体架构

    1. 使用GET、POST、PUT、DELETE这几种请求模式

    请求模式也可以说是动作、数据传输方式,通常我们在web中的form有GET、POST两种,而在HTTP中,存在下发这几种。

    GET (选择):从服务器上获取一个具体的资源或者一个资源列表。 

    POST (创建): 在服务器上创建一个新的资源。

    PUT(更新):以整体的方式更新服务器上的一个资源。 

    PATCH (更新):只更新服务器上一个资源的一个属性。

    DELETE(删除):删除服务器上的一个资源。 

    HEAD : 获取一个资源的元数据,如数据的哈希值或最后的更新时间。

    OPTIONS:获取客户端能对资源做什么操作的信息。

    常见的请求参数

    比如在数据过多, 需要对数据进行分页请求的时候, 我们应该统一 API 请求参数. 常见的有这些.

    limit=10指定返回记录的数量

    offset=10指定返回记录的开始位置。

    page=2&per_page=100指定第几页,以及每页的记录数。

    sortby=name&order=asc指定返回结果按照哪个属性排序,以及排序顺序。

    animal_type_id=1指定筛选条件

    2. 版本

    API的开发直接关系了APP是否可以正常使用,如果原本运行正常的API,突然改动,那么之前使用这个API的APP可能无法正常运行。APP是不可能强迫用户主动升级的,因此,通过API版本来解决这个问题。也就是说,API的多个版本是同时运行的,而且都要保证可以正常使用。

    按照RESTful的规范,不同的版本也应该用相同的API URL,通过header信息来判断版本,再调用不同版本的程序进行处理。但是这明显会给开发带来巨大的成本。

    解决办法有以下几种:

    1.新版本兼容旧版本,所有旧版本的动作、字段、操作,都在新版本中可以被实现,但明显这样的维护成本很大;

    2.不同的版本,用不同的URL来提供服务,在URL中通过v1、v2来区分版本号,比如v2.api.xxx.com/user的方式,或者http://api.domain.com/v2。

    3.每个接口有各自的版本,一般为接口添加个version的参数。

    3.json数据类型

    接口的数据一般都采用JSON格式进行传输,不过,需要注意的是,JSON的值只有六种数据类型:

    Number:整数或浮点数

    String:字符串

    Boolean:true 或 false

    Array:数组包含在方括号[]中

    Object:对象包含在大括号{}中

    Null:空类型

    所以,传输的数据类型不能超过这六种数据类型。以前,我们曾经试过传输Date类型,它会转为类似于"2016年1月7日 09时17分42秒 GMT+08:00"这样的字符串,这在转换时会产生问题,不同的解析库解析方式可能不同,有的可能会转乱,有的可能直接异常了。要避免出错,必须做特殊处理,自己手动去做解析。为了根除这种问题,最好的解决方案是用毫秒数或者字符串表示日期。

    4.返回的数据结构

    服务器返回的数据结构,一般为:

    {code:0,message:"success",data:{key1:value1,key2:value2,...}}

    code: 返回码,0表示成功,非0表示各种不同的错误

    message: 描述信息,成功时为"success",错误时则是错误信息

    data: 成功时返回的数据,类型为对象或数组

    不同错误需要定义不同的返回码,属于客户端的错误和服务端的错误也要区分,比如1XX表示客户端的错误,2XX表示服务端的错误。这里举几个例子:

    0:成功

    100:请求错误

    101:缺少appKey

    102:缺少签名

    103:缺少参数

    200:服务器出错

    201:服务不可用

    202:服务器正在重启

    错误信息一般有两种用途:一是客户端开发人员调试时看具体是什么错误;二是作为App错误提示直接展示给用户看。主要还是作为App错误提示,直接展示给用户看的。所以,大部分都是简短的提示信息。

    data字段只在请求成功时才会有数据返回的。数据类型限定为对象或数组,当请求需要的数据为单个对象时则传回对象,当请求需要的数据是列表时,则为某个对象的数组。这里需要注意的就是,不要将data传入字符串或数字,即使请求需要的数据只有一个,比如token,那返回的data应该为:

    //正确data:{token:123456}//错误data:123456

    5. 使用SSL(https)来提供URL

    首先,使用https可以在数据包被抓取时多一层加密。我们现在的APP使用环境大部分都是在路由器WIFI环境下,一旦路由器被入侵,那么黑客可以非常容易的抓取到用户通过路由器传输的数据,如果使用http未经加密,那么黑客可以很轻松的获取用户的信息,甚至是账户信息。

    其次,即使使用https,也要在API数据传输设计时,正确的采用加密。例如直接将token信息放在URL中的做法,即使你使用了https,黑客抓不到你具体传输的数据,但是可以抓到你请求的URL啊!(查了资料了,https用GET方式请求,也仅能抓到域名字符部分,不能抓到请求的数据,但是URL可以在浏览器或特殊客户端工具中直接看到。多谢下面的朋友指正错误)因此,使用https进行请求时,要采用POST、PUT或者HEAD的方式传输必要的数据。

    6.TOKEN

    现在,大部分App的接口都采用RESTful架构,RESTFul最重要的一个设计原则就是,客户端与服务器的交互在请求之间是无状态的,也就是说,当涉及到用户状态时,每次请求都要带上身份验证信息。实现上,大部分都采用token的认证方式,一般流程是:

    用户用密码登录成功后,服务器返回token给客户端;

    客户端将token保存在本地,发起后续的相关请求时,将token发回给服务器;

    服务器检查token的有效性,有效则返回数据,若无效,分两种情况:

    token错误,这时需要用户重新登录,获取正确的token

    token过期,这时客户端需要再发起一次认证请求,获取新的token

    然而,此种验证方式存在一个安全性问题:当登录接口被劫持时,黑客就获取到了用户密码和token,后续则可以对该用户做任何事情了。用户只有修改密码才能夺回控制权。

    如何优化呢?第一种解决方案是采用HTTPS。HTTPS在HTTP的基础上添加了SSL安全协议,自动对数据进行了压缩加密,在一定程序可以防止监听、防止劫持、防止重发,安全性可以提高很多。不过,SSL也不是绝对安全的,也存在被劫持的可能。另外,服务器对HTTPS的配置相对有点复杂,还需要到CA申请证书,而且一般还是收费的。而且,HTTPS效率也比较低。一般,只有安全要求比较高的系统才会采用HTTPS,比如银行。而大部分对安全要求没那么高的App还是采用HTTP的方式。

    我们目前的做法是给每个接口都添加签名。给客户端分配一个密钥,每次请求接口时,将密钥和所有参数组合成源串,根据签名算法生成签名值,发送请求时将签名一起发送给服务器验证。类似的实现可参考OAuth1.0的签名算法。这样,黑客不知道密钥,不知道签名算法,就算拦截到登录接口,后续请求也无法成功操作。不过,因为签名算法比较麻烦,而且容易出错,只适合对内的接口。如果你们的接口属于开放的API,则不太适合这种签名认证的方式了,建议还是使用OAuth2.0的认证机制。

    我们也给每个端分配一个appKey,比如Android、iOS、微信三端,每个端分别分配一个appKey和一个密钥。没有传appKey的请求将报错,传错了appKey的请求也将报错。这样,安全性方面又加多了一层防御,同时也方便对不同端做一些不同的处理策略。

    另外,现在越来越多App取消了密码登录,而采用手机号+短信验证码的登录方式,我在当前的项目中也采用了这种登录方式。这种登录方式有几种好处:

    不需要注册,不需要修改密码,也不需要因为忘记密码而重置密码的操作了;

    用户不再需要记住密码了,也不怕密码泄露的问题了;

    相对于密码登录其安全性明显提高了。

    7.用户设计

    显式用户和隐式用户,我不知道这两个词用的是否确切。 

    显式用户指的是,APP程序中有用户系统,一个username、password正确的合法用户,称之为显式的用户,

    通常显式用户都需要注册,登录以后能完成一些个人相关的操作。

    隐式用户指的是,APP程序本身就没有用户系统,或者一个在没有登录的情况下,使用我们APP的用户。

    在这种情况下,可以通过客户端生成的UDID来标识一个用户。

    有了用户信息,我们就能够了解不同用户的使用习惯,而不仅仅是全体用户的一个整体的统计信息,

    有了这些个体的信息之后,就可以做一些用户分群、个性化推荐之类的事情。

    如果是SAAS版本,还需要区分不同商户的用户

    8.良好的接口说明文档和测试程序

    接口文档有时候是项目初期就定下来的,前后端开发人员按照接口规范开发,

    有的是接口开发完成后写的。

    接口文档要清晰、明了,包含多少个接口,每个接口的地址、参数、请求方式、数据交换格式、返回值等都要写清楚。

    接口测试程序,有条件的话,也可以提供,方便前后端的调试。

    如果是springMVC开发的话,可以用swagger,后端只要加几个注释发一个url给前端就可以了,轻松又高效。

    9.接口统计功能

    在做PC端网站的时候,我们都会给我们的网站加上个统计功能,要么自己写统计系统,要么使用第三方的比如GA、百度等。

    移动端接口API则需要我们自己实现统计功能,

    这时就需要我们尽可能多的收集客户端的信息,除了传统的IP、User-Agent之外,还应该收集一些移动相关的信息,

    比如

    手机操作系统,是android还是ios,都是什么版本,

    用户使用的网络状况,是2G、3G、4G还是WIFI

    客户端APP是什么版本信息。

    这样,有助于我们更好的了解我们用户的使用情况。

    相关文章

      网友评论

          本文标题:RESTful api接口安全优雅设计

          本文链接:https://www.haomeiwen.com/subject/utsfoxtx.html