首页 存档 技术 查看内容

RESTfulAPI身份认证中的安全性设计

2018-3-30 13:00 |来自: 互联网 267 0

摘要: 本文选自《开发者头条》1 月 12 日最受欢迎文章 Top 3,感谢作者 周梦康 分享。 欢迎分享:http://toutiao.io/contribute REST是一种软件架构风格。RESTful Api 是基于 HTTP 协议的 Api,是无状态传输。它的核心是将 ...

本文选自《开发者头条》1 月 12 日最受欢迎文章 Top 3,感谢作者 周梦康 分享。

欢迎分享http://toutiao.io/contribute

REST是一种软件架构风格。RESTful Api 是基于 HTTP 协议的 Api,是无状态传输。它的核心是将所有的 Api 都理解为一个网络资源。将所有的客户端和服务器的状态转移(动作)封装到 HTTP 请求的 Method 之中。


详情可以阅读 http://mengkang.net/620.html 。


而本篇文章则主要是讨论 RESTful Api 身份认证安全性设计。


没有绝对的安全,这个话题很深,下文都是自己的一些理解,水平有限,如有勘误,希望大家予以指正。


由于 RESTful Api 是基于 Http 协议的 Api,是无状态传输,所以只要和用户身份有关的请求都会带上身份认证信息。(很多时候客户端事先并不知道某个 api 后期会不会加入身份判断,所以我们一般都会选择每个请求都会带上认证信息,如果有的话。)


Http Basic Authentication

Http Basic 是一种比较简单的身份认证方式。在 Http header 中添加键值对 Authorization: Basic xxx (xxx 是 username:passowrd base64 值)。


例如 username 为 zmk ,password 为 123456,请求则如下

GET /auth/basic/ HTTP/1.1
Host: xxxxx
Authorization: Basic em1rOjEyMzQ1Ng==


而 Base64 的解码是非常方便的,如果不使用 Https ,相当于是帐号密码直接暴露在请求中。


危险性高,实际开发者使用的应该几乎为0。


顺便提下 DIGEST 认证,和 BASIC 认证相差无几,而且不适合 api 设计,实际又需要两次请求,首次请求,服务器端返回401,并且带上nonce值,然后客户端再利用username password nonce默认MD5之后再请求。对 http 请求的作用是仅仅防止二次请求,对身份认证并没有什么提升。


Cookie Session

不知道是否应该这么称呼,只是觉得类似于cookie与session的机制。


原理即当客户端登录完毕之后,给客户端返回一个cookie,服务器端控制该session的有效期,每次请求都带上该值,然后服务器端做验证,退出之后,客户端通知服务端端销毁session,自身销毁cookie。但是如果抓包获取到cookie,就能任意伪造请求了。


危险性高,实际开发估计使用得还不少。


Api Key Security Key Sign

下图是我们自己每次请求的身份认证的方式,如有不足,请大家指出。可以说是 JWT 的自定义版吧。

这里的认证逻辑即:

  1. 用户登录返回一个api_key和security_key;

  2. 然后客户端将security_key存在客户端;

  3. 当要发送请求之前,通过function2加密方法,把如图所示的五个值一起加密,得到一个sign;

  4. 发送请求的时候,则将除去security_key之外的值,以及sign一起发送给服务器端;

  5. 服务器端首先验证时间戳是否有效,比如是服务器时间戳5分钟之前的请求视为无效;

  6. 然后根据api_key验证sercurity_key;

  7. 最后验证sign。


是否需要加上时间戳验证?

上面的认证逻辑中加密得到签名的时候,把时间戳加进去是为了在一定程度上屏蔽了一些无效的请求,可以略去,也可以设计的更加严格。如果想防止恶意的 api ddos 攻击,这一步验证肯定是不行的。需要做更多的验证,比如用户验证,ip 验证等。可以参考 github 的 api 的设计。它会在返回的 http 头信息里带上


X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4999


表示这个接口在某一时间段内,该授权用户调用该接口的最大次数为5000次,该时间段内还剩余4999次。当然,这样的验证加上之后,在代码的执行效率上肯定会有所影响。


是否需要将request_parameters也加入到sign生成的算法之中?

也不是必须的,仅仅是为了请求的真实性,减少请求的伪造,比如有人抓包拿到 http 请求之后,如果没有验证sign这步,那么别人就可以非常简单的修改请求的参数,而请求都会生效。


血的教训,自己经历的一个实际案例:

一个取消用户喜欢的标签的接口,该接口会向服务器端发送类似于ids=1,2,3,4这样的request_parameters,然后服务器端拿到这些 id 之后切割,然后将该用户和这些标签的关系从user_tag表中删除。某个周末,数据库服务器报警,而依照我们用户习惯,那个时间不存在流量高峰,这个报警很不正常,正准备处理,报警结束了,但是过了一段时间就有用户反应他们喜欢的标签都被删了。

通过查询数据库的慢日志,发现有很多注入的 sql。


DELETE FROM `user_tag` WHERE uid=4385328 AND tid=1 OR 14=14;
DELETE FROM `user_tag` WHERE uid=4385328 AND tid=1 OR 91=91;


原来没有对切割之后的 id 没有做数字验证,估计黑客就是传的ids=1 OR 14=14,2,3,而一个 delete 操作可能超时,他丫的就搞了很多次请求,真是够狠的。

幸运,数据库还有定时的打包备份,大部分用户的数据还是恢复了,同时修复了这一漏洞。


所以如果这里将request_parameters也加入到签名之中,就减少了伪造请求的可能性,但是无法杜绝,破坏者可能就非要黑你,又对逆向工程非常熟悉,找到我们加密算法的实现,依然可以未知出合法的签名,所以我们常说,服务器端永远不能相信客户端的请求都是安全的、合法的,需要做验证的都还是不能省略。


同时这(sign算法)也造成了 api 接口调试的成本,api 测试工具必须也得实现那一套算法,或者是设置在开发环境下不做验证。我们在配置开发环境的时候则是 vpn 连测试服务器所在内网,然后进行测试,否则开发环境也存在被人利用的风险。

项目实例 https://github.com/zhoumengkang/netty-restful-server


JWT

JWT (JSON Web Token) 使用流程如下(图片来自官网)

其认证机制也是登录,发放密钥给客户端,然后客户端每次发送请求的时候通过 JWT 的算法规则组装 JWT 的Auth Header,服务器端作验证。


web 授权认证的原理万变不离其宗,都是如此。


只不过 JWT 呢,自定了一套认证协议。格式为Header.Payload.Signature。比如xxxxx.yyyyy.zzzzz。签名内容是有Header Payload Secret通过HMAC SHA256算法加密而成。


HMACSHA256(
  base64UrlEncode(header)   "."  
  base64UrlEncode(payload),
  secret)


而请求的很多参数键值对都可以放在Payload里面。完整讲解请求看官方的介绍http://jwt.io/introduction/


需要注意的一点,依照 JWT 的协议,只有一个secret,无法得知该用户是谁,所以在secret该值中必须要可以解码出用户的id。


而我们自定义认证协议的时候header感觉就没有必要了,使用什么算法事先定义好即可。所以我们也没选择这种方式而是上面的那种方式。

更多优质内容,欢迎安装、使用《开发者头条》iOS、Android 客户端。

体验地址http://toutiao.io/download

本文转载自:微信公众账号 - developerWorks,版权归原作者所有!

声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部