HTTP API 认证授权术
我们知道,HTTP是无状态的,所以,当我们需要获得用户是否在登录的状态时,我们需要检查用户的登录状态,一般来说,用户的登录成功后,服务器会发一个登录凭证(又被叫作Token),就像你去访问某个公司,在前台被认证过合法后,这个公司的前台会给你的一个访客卡一样,之后,你在这个公司内去到哪都用这个访客卡来开门,而不再校验你是哪一个人。在计算机的世界里,这个登录凭证的相关数据会放在两种地方,一个地方在用户端,以Cookie的方式(一般不会放在浏览器的Local Storage,因为这很容易出现登录凭证被XSS攻击),另一个地方是放在服务器端,又叫Session的方式(SessonID存于Cookie)。
但是,这个世界还是比较复杂的,除了用户访问,还有用户委托的第三方的应用,还有企业和企业间的调用,这里,我想把业内常用的一些 API认证技术相对系统地总结归纳一下,这样可以让大家更为全面的了解这些技术。注意,这是一篇长文!
本篇文章会覆盖如下技术:
- HTTP Basic
- Digest Access
- App Secret Key + HMAC
- JWT – JSON Web Tokens
- OAuth 1.0 – 3 legged & 2 legged
- OAuth 2.0 – Authentication Code & Client Credential
目录
HTTP Basic
HTTP Basic 是一个非常传统的API认证技术,也是一个比较简单的技术。这个技术也就是使用 username
和 password
来进行登录。整个过程被定义在了 RFC 2617 中,也被描述在了 Wikipedia: Basic Access Authentication 词条中,同时也可以参看 MDN HTTP Authentication
其技术原理如下:
- 把
username
和password
做成username:password
的样子(用冒号分隔) - 进行Base64编码。
Base64("username:password")
得到一个字符串(如:把haoel:coolshell
进行base64 后可以得到aGFvZW86Y29vbHNoZWxsCg
) - 把
aGFvZW86Y29vbHNoZWxsCg
放到HTTP头中Authorization
字段中,形成Authorization: Basic aGFvZW86Y29vbHNoZWxsCg
,然后发送到服务端。 - 服务端如果没有在头里看到认证字段,则返回401错,以及一个个
WWW-Authenticate: Basic Realm='HelloWorld'
之类的头要求客户端进行认证。之后如果没有认证通过,则返回一个401错。如果服务端认证通过,那么会返回200。
我们可以看到,使用Base64的目的无非就是为了把一些特殊的字符给搞掉,这样就可以放在HTTP协议里传输了。而这种方式的问题最大的问题就是把用户名和口令放在网络上传,所以,一般要配合TLS/SSL的安全加密方式来使用。我们可以看到 JIRA Cloud 的API认证支持HTTP Basic 这样的方式。
但我们还是要知道,这种把用户名和密码同时放在公网上传输的方式有点不太好,因为Base64不是加密协议,而是编码协议,所以就算是有HTTPS作为安全保护,给人的感觉还是不放心。
Digest Access
中文称“HTTP 摘要认证”,最初被定义在了 RFC 2069 文档中(后来被 RFC 2617 引入了一系列安全增强的选项;“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数)。
其基本思路是,请求方把用户名口令和域做一个MD5 – MD5(username:realm:password)
然后传给服务器,这样就不会在网上传用户名和口令了,但是,因为用户名和口令基本不会变,所以,这个MD5的字符串也是比较固定的,因此,这个认证过程在其中加入了两个事,一个是 nonce
另一个是 qop
- 首先,调用方发起一个普通的HTTP请求。比如:
GET /coolshell/admin/ HTTP/1.1
- 服务端自然不能认证能过,服务端返回401错误,并且在HTTP头里的
WWW-Authenticate
包含如下信息:
WWW-Authenticate: Digest realm="[email protected]", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
- 其中的
nonce
为服务器端生成的随机数,然后,客户端做HASH1=MD5(MD5(username:realm:password):nonce:cnonce)
,其中的cnonce
为客户端生成的随机数,这样就可以使得整个MD5的结果是不一样的。 - 如果
qop
中包含了auth
,那么还得做HASH2=MD5(method:digestURI)
其中的method
就是HTTP的请求方法(GET/POST…),digestURI
是请求的URL。 - 如果
qop
中包含了auth-init
,那么,得做HASH2=MD5(method:digestURI:MD5(entityBody))
其中的entityBody
就是HTTP请求的整个数据体。 - 然后,得到
response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)
如果没有qop
则response = MD5(HA1:nonce:HA2)
- 最后,我们的客户端对服务端发起如下请求—— 注意HTTP头的
Authorization: Digest ...
GET /dir/index.html HTTP/1.0 Host: localhost Authorization: Digest username="Mufasa", realm="[email protected]", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="%2Fcoolshell%2Fadmin", qop=auth, nc=00000001, cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41"
维基百科上的 Wikipedia: Digest access authentication 词条非常详细地描述了这个细节。
摘要认证这个方式会比之前的方式要好一些,因为没有在网上传递用户的密码,而只是把密码的MD5传送过去,相对会比较安全,而且,其并不需要是否TLS/SSL的安全链接。但是,别看这个算法这么复杂,最后你可以发现,整个过程其实关键是用户的password,这个password如果不够得杂,其实是可以被暴力破解的,而且,整个过程是非常容易受到中间人攻击——比如一个中间人告诉客户端需要的 Basic 的认证方式 或是 老旧签名认证方式(RFC2069)。
App Secret Key + HMAC
先说HMAC技术,这个东西来自于MAC – Message Authentication Code,是一种用于给消息签名的技术,也就是说,我们怕消息在传递的过程中被人修改,所以,我们需要用对消息进行一个MAC算法,得到一个摘要字串,然后,接收方得到消息后,进行同样的计算,然后比较这个MAC字符串,如果一致,则表明没有被修改过(整个过程参看下图)。而HMAC – Hash-based Authenticsation Code,指的是利用Hash技术完成这一工作,比如:SHA-256算法。
(图片来自 Wikipedia – MAC 词条 )
我们�