当我们在提供一个服务时,除了面向用户提供界面操作外,还会面向各种三方开发者,那么此时服务的接口认证就很重要了。
下面来简单说说怎么设计一个接口的认证:API签名机制
API签名可以理解为就是对API的调用进行签名保护。是在进行API调用时,加了一个调用者及其调用行为的指纹信息,以帮助服务端更好的识别用户及其调用行为的合法性。其直接目的归纳为:
(1)明确调用者的身份(确认调用者是谁)
(2)明确调用者的调用行为(确认调用者想要做什么)
由此可见,API签名的真正目的是:通过明确调用者的身份,以便控制API的访问权限,从而保护数据的安全性。
身份标识
我们都知道,在程序的世界中,很难找到一个稳定且唯一的信息去标识一个调用者,因为调用者本身的信息(如IP、设备等)也是不固定的,所以,标识调用者最好的方法就是服务端统一分配,具体过程大致如下:
调用者调用API前,必须向系统申请一个唯一的标识
系统为每个调用者分配一个唯一的ID,这里暂定为SecretID
调用者调用API时带上该SecretID
服务端 通过SecretID确认调用者身份
以上流程的问题,在于SecretID是明文显示的,很容易被窃取和伪造;但SecretID又不能隐藏或加密,因为SecretID需要明确告诉服务端:我是谁?
所以,需要在SecretID之外,增加一个和SecretID绑定的信息,我们称之为:用户密钥
用户密钥(即SecretKey)就是为了验证用户身份用的,为了提高其安全性能,必须保证
调用者必须保护好SecretKey,不能在任何地方明文显示
SecretKey最好不在请求过程中传输
操作行为
如何知道用户的调用行为呢?一般就是把调用行为涉及的关键信息都放到签名内容中进行签名。那么,哪些是关键信息呢?
请求的方法和接口
即每个请求Method和URL,这是每个请求都有的信息,且最为关键的信息。请求的内容
请求内容一般指HEADERS、QueryString、BODY三大类。
那么,哪一类内容需要添加的签名内容中呢?
一个简单的判断标准,就是看这一块的内容的变更是否影响请求结果,若影响了,一般要求加入到签名内容中;若设计时还不确定,则全部内容加到签名内容中即可。
防重放
对于恶意攻击者,截取一个合法请求后,不停地使用该请求对服务端进行攻击;这种攻击可能造成重访攻击。
实现的方法,也很简单,那就是调用者每次调用时:
调用者生成并带上一个随机数Nonce
服务端该随机数是否已出现,有则拒绝,无则存储该随机数并放过请求
这里服务端要保证Nonce唯一,就得存储已经用过的Nonce,但长期保持会带来两个问题
(1)存储成本增加,日积月累,这里要存储的Nonce会越来越多,需要的存储空间就越大
(2)碰撞概率增加,正常服务被拒绝概率增大;这里随着生成Nonce值越来越多,碰撞的概率一定越来越大,若通过增加Nonce值的长度,有增加存储成本。
那么,另一个可行的办法,就是调用者每次请求时带上当前请求时间点Timestamp,然后由服务端限制请求的时效性。
即某个请求,其请求时间戳Timestamp,和服务端的当前时间在规定时间内(如1分钟内)则为合法请求,反之,则视为无效请求。
如此,上面提到的Nonce值存储成本可能比较大的问题,在结合Timestamp后,可大大降低存储成本,如Timestamp=1min,则仅需存储1min内的请求Nonce值即可,大大减少存储的量级。
版本控制
另外,每个设计都很难做到完美,或者当前看已经比较完善,但随着技术的发展,会逐渐的暴露一些缺陷,此时,想做一个可持续发展的API签名方案,版本迭代自然少不了,所以,请求内容也可加上版本信息。
Token区别
Token和API签名方案虽然解决的问题类似,但应用场景略有不同。
两者最大的不同在于校验信息的适用范围不同,而由此带来的安全方面的差距就比较明显
API签名方案:一个请求对应一个签名信息。若API签名信息被窃取后,坏人是无法据此发起更多有效攻击的
Token方案:多个请求(即一段时间内的请求)对应一个Token。若Token信息被窃取后,坏人是可以据此伪造一系列请求进行攻击的两者的第二大不同在于,密钥信息的使用不同,由此带来使用场景方面的不同
Token方案,有个登录的过程,用户输入一次密钥,换回票据即可;后续请求无需密钥参与,使用票据即可
API签名方案,每次请求都需要密钥参与,绝不可能每次请求都让用户输入密钥,那么就要求发起方存储密钥;若是js、app等纯前端场景存储密钥会有安全问题
API签名适用于后台对后台,不适用于前后端对接。
因为前端场景比较难解决密钥存储问题(且一般面对不同的用户),所以一般都是用户身份一次验证,多次使用(即Token方案),至于用户身份认证可使用其他手段(如密码、短信验证码、图像验证码、邮件验证码等)验证。
Jwt区别
具体区别大概如下:
API签名方案中,私钥由客户端保存,服务端验证的是客户端的签名(即客户端的身份)。
JWT方案,私钥由服务端保存,服务端验证的是自己的签名(自己的身份),间接验证客户端的身份(因为服务端先通过其他方式验证客户端的身份,验证成功后签发JWT给客户端,后续验证的JWT其实是服务端的签名信息,通过验证自己派发票据的正确性来证明客户端的身份)。
JWT本质上就是一种Token方案,