API(Application Programming Interface,应用程序接口)是一些预先定义的接口(如函数、HTTP接口),或指软件系统不同组成部分衔接的约定。用来提供应用程序与开发人员基于某软件或硬件得以访问的一组例程,而又无需访问源码,或理解内部工作机制的细节。

------- 百度百科

API是应用程序接口的统称,而我们今天要聊的就是其中的HTTP服务接口。


前后端分离

不同于传统的MVC项目,前后端分离架构将数据的处理与渲染完全分开。服务器与客户端各司其职,从而最大程度的减小项目开发与维护难度,同时由客户端渲染数据,可以将服务器的压力减到最小。
通常大家一致认同的前后端分离例子就是SPA应用(Single-page application),SPA是从物理层面做出区分,这样的做法太过于片面。在实际开发中,SPA类应用占的比例很小,很多WEB服务场景下还需要用到同步/同步+异步混合的模式,因此从职责上划分才更加符合实际的应用场景。后端负责Model层,并通过API接口来提供数据;前端负责View和Controller层,自行渲染后端数据。

常见的WEB攻击方式

常见的web攻击方式有:XSS、CSRF、SQL注入、DDOS、重放攻击、篡改请求等。
XSS(跨站脚本攻击):对所有用户提交的内容进行合法性验证,过滤可能存在的脚本内容。
CSRF(跨站请求伪造):接口尽量使用POST方式,cookie设置为HttpOnly,验证token以及referer等。
重放攻击:增加时效性验证,增加随机字符验证。
篡改:尽量使用https协议,对请求参数进行验证。

保障接口安全

1、token鉴权(防止CSRF)
token是客户端访问服务器的凭证。
用户登录成功后由服务端生成专属的加密token,并返回给客户端。客户端存储token,后续请求都需要携带token,服务端接收token后解密,并对token内容做出合法性判断。完整方案可以参考JWT(Json Web Token)。

2、时效性验证(防重放和DDos攻击)
通过timestamp和nonce来限制请求。
根据情况设置一个有效时常(例如60s),当服务器接收到请求时首先拿timestamp与服务器时间进行对比,如果大于60s则判定为请求无效。
如果仅通过timestamp来限制肯定是不够的,黑客们仍然可以在60s之内发动重放攻击,所以我们引入一个新的参数nonce(随机字符串)。
客户端每次请求都应该携带一个随机字符串,服务端接收后通过临时文件或redis等方式存储nonce,如果后续请求携带的nonce已存在,则判定为请求无效。
因为timestamp有效期为60s,为了防止数据量过大,我们可以将60s之前的nonce删除。

3、签名验证(防篡改)
把所有的请求参数按照特定的顺序进行排序生成字符串data,将uid、token、timestamp、nonce和data通过md5加密,生成参数sign。
每次请求都携带sign,服务器接收请求后以同样的方法生成sign,如果md5值不同,则表示数据遭到篡改。

完整的流程

1、 客户端使用用户名和密码请求服务器并获取token,缓存在本地

2、 客户端生成timestamp和nonce,将请求参数按照特定顺序进行排序,生成一个字符串

3、 客户端将token、timestamp、nonce、uid和请求参数进行md5加密生成参数sign

4、 将token、timestamp、nonce、sign添加到每个请求的url中,要传递的参数则放到post数据中。例如:https://api.ui.ac.cn/user/info?token=xxx×tamp=xxx&nonce=xxx&sign=xxx
(这样看起来url太长,所以我们可以将token、timestamp、nonce放到请求header中)

5、 服务器按照同样的方法生成参数sign,通过过滤器对token、timestamp、sign等参数进行检验,只有在所有要求都符合的情况下才响应这次请求。

在以上三层保护下,只要服务端token密匙不泄露,基本可以防止客户端请求被劫持、伪造、重放、篡改。

最后:就像世界没有绝对的公平一样,服务器也没有绝对的防御,所有的防护措施都只是为了提高攻击的成本。另外,所有的防护措施都用上的话有时候难免太过复杂,我们应该根据实际使用情况做出相应的权衡。