restful 安全认证API 用户登陆认证功能如何设计

WCF RESTful Web API 应如何做认证?
[问题点数:200分]
WCF RESTful Web API 应如何做认证?
[问题点数:200分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2016年1月 总版技术专家分月排行榜第二2015年11月 总版技术专家分月排行榜第二2015年10月 总版技术专家分月排行榜第二
优秀小版主
2015年9月 总版技术专家分月排行榜第二
2015年8月 总版技术专家分月排行榜第三
匿名用户不能发表回复!|REST是一种软件架构风格。RESTful Api 是基于 HTTP 协议的 Api,是无状态传输。它的核心是将所有的 Api 都理解为一个网络资源。将所有的客户端和服务器的状态转移(动作)封装到 HTTP 请求的 Method
详情可以阅读
而这篇文章则主要是讨论 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 的解码是非常方便的,如果不使用
,相当于是帐号密码直接暴露在请求中。
危险性高,实际开发者使用的应该几乎为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 的自定义版吧。
这里的认证逻辑即:
用户登录返回一个 api_key 和 security_key ;
然后客户端将 security_key 存在客户端;
当要发送请求之前,通过 function2 加密方法,把如图所示的五个值一起加密,得到一个 sign ;
发送请求的时候,则将除去 security_key 之外的值,以及 sign 一起发送给服务器端;
服务器端首先验证时间戳是否有效,比如是服务器时间戳5分钟之前的请求视为无效;
然后根据 api_key 验证 sercurity_key ;
最后验证 sign 。
是否需要加上时间戳验证?
上面的认证逻辑中加密得到签名的时候,把时间戳加进去是为了在一定程度上屏蔽了一些无效的请求,可以略去,也可以设计的更加严格。 如果想防止恶意的 api ddos 攻击,这一步验证肯定是不行的。需要做更多的验证,比如用户验证,ip 验证等。 。它会在返回的 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 连测试服务器所在内网,然后进行测试,否则开发环境也存在被人利用的风险。
JWT ( ) 使用流程如下(图片来自官网)
其认证机制也是登录,发放密钥给客户端, 然后客户端每次发送请求的时候通过 JWT 的算法规则组装 JWT 的 Auth Header ,服务器端作验证。
web 授权认证的原理万变不离其宗,都是如此。
只不过 JWT 呢,自定了一套认证协议。格式为 Header . Payload . Signature 。比如 xxxxx.yyyyy.zzzzz 。签名内容是有 Header + Payload + Secret 通过 HMAC SHA256 算法加密而成。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
而请求的很多参数键值对都可以放在 Payload 里面。完整讲解请求看官方的介绍
需要注意的一点 ,依照 JWT 的协议,只有一个 secret ,无法得知该用户是谁,所以在 secret 该值中必须要可以解码出用户的id。
而我们自定义认证协议的时候 header 感觉就没有必要了,使用什么算法事先定义好即可。所以我们也没选择这种方式而是上面的那种方式。
一个基于 netty 的严格的安全验证的轻量级的 RESTful Api Server404 Not Found
The requested URL /q/3648/ was not found on this server.404 Not Found
The requested URL /q/3648/a-4139 was not found on this server.常用认证方式
在之前的文章与两篇文章中,中也详细介绍,一般基于REST API 安全设计常用方式有: HTTP Basic
Basic admin:admin
Basic YWRtaW46YWRtaW4=
Authorization: Basic YWRtaW46YWRtaW4=
由于HTTP协议是无状态的,所有每次请求都得带上身份信息,基于Http basic验证就是简单的将用户名和密码base64编码放到header中,一般需要HTTPS,安全性较低,实现的方式可以基于代码实现也可以基于web容器配置apache,nginx等web服务器即可实现。
HTTP Digest
摘要认证 digest authentication,服务器端以nonce进行质询,客户端以用户名,密码,nonce,HTTP方法,请求的URI等信息为基础产生的response信息进行认证的方式。
&&& ※ 不包含密码的明文传递
&&& 摘要认证步骤:
&&&& 1. 客户端访问一个受http摘要认证保护的资源。
&&&& 2. 服务器返回401状态以及nonce等信息,要求客户端进行认证。
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest
qop="auth,auth-int",
nonce="dcd98be8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9fe41"
&&&& 3. 客户端将以用户名,密码,nonce值,HTTP方法, 和被请求的URI为校验值基础而加密(默认为MD5算法)的摘要信息返回给服务器。
&&&&&&&&&& 认证必须的五个情报:
? realm : 响应中包含信息
? nonce : 响应中包含信息
? username : 用户名
? digest-uri : 请求的URI
? response : 以上面四个信息加上密码信息,使用MD5算法得出的字符串。
Authorization: Digest
username="Mufasa", & 客户端已知信息
realm="", & 服务器端质询响应信息
nonce="dcd98be8b11d0f600bfb0c093", & 服务器端质询响应信息
uri="/dir/index.html", & 客户端已知信息
qop=auth, & 服务器端质询响应信息
nc=, & 客户端计算出的信息
cnonce="0a4f113b", & 客户端计算出的客户端nonce
response="6629faeef1", & 最终的摘要信息 ha3
opaque="5ccc069c403ebaf9fe41" & 服务器端质询响应信息
&&&& 4. 如果认证成功,则返回相应的资源。如果认证失败,则仍返回401状态,要求重新进行认证。
&&&& 注意事项:
&&&& 1. 避免将密码作为明文在网络上传递,相对提高了HTTP认证的安全性。
&&&& 2. 当用户为某个realm首次设置密码时,服务器保存的是以用户名,realm,密码为基础计算出的哈希值(ha1),而非密码本身。
&&&& 3. 如果qop=auth-int,在计算ha2时,除了包括HTTP方法,URI路径外,还包括请求实体主体,从而防止PUT和POST请求表示被人篡改。
&&&& 4. 但是因为nonce本身可以被用来进行摘要认证,所以也无法确保认证后传递过来的数据的安全性。
&& ※ nonce:随机字符串,每次返回401响应的时候都会返回一个不同的nonce。
&& ※ nounce:随机字符串,每个请求都得到一个不同的nounce。
&&&&& ※ MD5(Message Digest algorithm 5,信息摘要算法)
&&&&&&&& 1)用户名:realm:密码 &rA ha1
&&&&&&&& 2)HTTP方法:URI &rA ha2
&&&&&&&& 3)ha1:nonce:nc:cnonce:qop:ha2 &rA ha3
WSSE(WS-Security)
&&& WSSE UsernameToken
&&& 服务器端以nonce进行质询,客户端以用户名,密码,nonce,HTTP方法,请求的URI等信息为基础产生的response信息进行认证的方式。
&&& ※ 不包含密码的明文传递
&&& WSSE认证步骤:
&&&& 1. 客户端访问一个受WSSE认证保护的资源。
&&&& 2. 服务器返回401状态,要求客户端进行认证。
HTTP/1.1 401 Unauthorized
WWW-Authenticate: WSSE
profile="UsernameToken" & 服务器期望你用UsernameToken规则生成回应
※ UsernameToken规则:客户端生成一个nonce,然后根据该nonce,密码和当前日时来算出哈希值。
&&&& 3. 客户端将生成一个nonce值,并以该nonce值,密码,当前日时为基础,算出哈希值返回给服务器。
Authorization: WSSE profile="UsernameToken"
X-WSSE:UsernameToken
username="Mufasa",
PasswordDigest="Z2Y......",
Nonce="dcd98be8b11d0f600bfb0c093",
Created="T09:00:00Z"
&&&& 4. 如果认证成功,则返回相应的资源。如果认证失败,则仍返回401状态,要求重新进行认证。
一般会分配app_key,sign_key两个值,将通知过来的所有参数,除去sign本身,以及值是空的参数,按参数名字母升序排序,然后按参键值&参数n值n的方式进行连接,
得到一个字符串然后在连接后得到的字符串前面加上通知验证密钥(sign_key, 不同于app_key),然后计算sha1值,转成小写
比如请求的参数为:
?sign=9a48ac7f0d185c525ee965e591a7&verifycode=&app_key=ca2bf41f370ebf87caeafd&poiid=12345&time
stamp=&poiname= 海底捞(朝阳店)&v=1
去掉sign参数,其余的按参数名升序排列:app_keyca2bf41f370ebf87caeafdpoiid12345poiname海底捞(朝阳店)timestampv
1verifycode
假设sign_key为21beabc81aa945a02bec3,我们把sign_key放在上面的字符串的前面:21beabc81aa945a02bec37601c
f3cc21app_keyca2bf41f370ebf87caeafdpoiid12345poiname海底捞(朝阳店)timestampv1verifycode
计算sha1()结果为:9a48ac7f0d185c525ee965e591a7
微信V3支付与美团券(以上是美团核销券文档摘录)采用此种方式,对外有一个统一的服务网关,一般如果设计到安全性重要的接口,再加上数字证书(如微信支付的退款接口),适用服务端对服务端的应用。
OATH2设计更侧重对资源的授权,OAUTH2规范定义了Authorization Code,Resource Owner Password Credentials,Client Credentials ,Implicit Grant几种实现方式,具体看: ,Authorization Code 模式侧重对第三方用户资源的授权(如QQ联合登录,微博开放平台等),Resource Owner Password Credentials 侧重对个人用户资源授权(如App),Client Credentials 侧重对客户端的资源授权,Implicit Grant 是一种最简化模式,如网页中JS中调用,适用场景比较局限。
1) 先由客户端向服务器发出一个验证请求。
2) 服务器接到此请求后生成一个随机数并通过网络传输给客户端(此为质疑)。
3) 客户端将收到的随机数提供给ePass,由ePass使用该随机数与存储在ePass中的密钥进行HMAC-MD5运算并得到一个结果作为认证证据传给服务器(此为响应)。
4) 与此同时,服务器也使用该随机数与存储在服务器数据库中的该客户密钥进行HMAC-MD5运算,如果服务器的运算结果与客户端传回的响应,结果相同,则认为客户端是一个合法用户
JWT 是JSON Web Token简写,用于发送通过签名和认证的东西,服务端可通过解析该值来验证是否有操作权限,是否过期等安全性检查等。
HTTP Digest与WSSE认证来源
支付接口安全性[服务端对服务端]
支付宝:统一接口网关,RSA双向签名验证
微信:API KEY方式+数字证书
银联:RSA+数字证书
App适用的授权方式
跟用户没有关系的资源 Client Credentials,HMAC或API KEY,如果Token被抓包或APIKEY被反编译,不保证安全性,但是可以杜绝大部分的垃圾请求。
跟用户有关系的资源 Resource Owner Password Credentials
基于JWT实现
基于Resource Owner Password Credentials 自定义实现发放Token
ASP.NET WBAPI资源安全设计
Securing ASP.NET Web APIs
Pro ASP.NET Web API Security
服务设计参考
API V3 上线,实现 OAuth 2 认证
有赞 API 文档【appkey与oauth2】
REFER: [译]web权限验证方法说明
开放接口的安全验证方案(AES+RSA)
RESTful Api 身份认证安全性设计
大话接口隐私与安全https://blog.thankbabe.com//donot-touch-my-url/
API 客户端认证那些事
REST API Authentication
Best Practices for securing a REST API / web service
Best practices for API versioning
阅读(...) 评论()

我要回帖

更多关于 restful api 认证 的文章

 

随机推荐