引子
网上现在介绍OAuth时,大多是把OAuth core中的3 steps和一张summary图copy过来完事。
OAuth Authentication is done in three steps:
- The Consume r obtains an unauthorized Request Token.
- The User authorizes the Request Token.
- The Consumer exchanges the Request Token for an Access Token.
有图有真相:http://oauth.net/core/diagram.png
好吧,
如果你能拿起笔来顺手就画出3steps,理解就基本ok。
如果你可以自己写个oauth client,整个spec的细节都已掌握。
如果你可以自己实现oauth server,则算是融汇贯通了。
如果你知道oauth的安全漏洞和微弱环节,那么你肯定是安全专家。
本篇文章的目标是第一个,帮你理解oauth。
My understanding of OAuth
what's the problem it solve?
3 legs problem. 随着web2.0的兴起,大量的网站都提供了API来暴露用户的数据给第三方应用。web2.0是啥,就是UGC(user generated content),从blog, photo到现在的sns,微博。大量的用户产生了大量的数据,这些网站渐渐演变成一个平台。第三方可以接入平台,开发自己的应用。而接入,就是调用平台提供的API得到用户的数据,衍生自己的应用。
用例一:用户使用每个网站和应用时,首先需要注册,填入ID,登录密码,邮箱,姓名等等个人资料。如果是在某开放平台上开发,则可省去这一环,用户登录平台后可授权应用开发商,将自己的个人资料从平台中导入。(openid + oauth 提供了完整的用户免注册问题)
用例二:照片打印服务(官网中的例子)。有用户在Flick或者Picassa上传了一次在三藩旅游的照片,然后他想通过另外一个照片打印服务商(比较便宜)将这次旅游的照片打印成册。
在Basic auth时代,用户直接将自己的username和password告诉给第三方应用。比如名单导入和好友邀请,网站会提示:请输入msn用户和密码,本网站将保证不会泄漏.....
直接交出用户名和密码无疑是很危险的事
- No control of the access. "本网站保证不会泄漏....",不代表该网站不会在取得你的联系人名单时,顺带定期读取你的email,分析email内容,给你投放定向广告。打印服务商可能不光是将三藩旅游的照片打印出来,可能你XXOO的也一并捎带。
不交出用户名和密码,如何授权给应用?
- oauth is try to solve the delegation (authorization) problem in a secure way in non https-environments
Some knowledge about security
oauth问题很简单,然而过程却有点trouble。要理解这些过程,首先补充下关于计算机网络安全的知识。
数字签名
在现实生活中,我们使用银行卡时,需要卡号(ID)和密码;网站登录需要ID(user)和Password。ID是用户唯一标认,而password则是用户身份验证(authentication)。
数字签名的作用一样:A valid digital signature gives a recipient reason to believe that the message was created by a known sender, and that it was not altered in transit.
一是验证文档用户(authentication)的身份,二是保证其没被更改(integration) -- 好比我们手填一些单子时,不容许有涂改。
怎样做呢?把用户的ID和password连同文档一块发出去? 搞计算机密码学的不同意!
- unencrypted password is not allowed in computer cryptology
我们使用信用卡时,无需透露信用卡给商家,只需一个签名即可。密码学的同仁采取了类似的做法:
- 使用hash fucntion生成文档(数据)的一个hash值。具体的hash函数不作深究,比如:取模hash,可以将所有data的byte直接相加,然后取个模。
- 用一个private key将hash值加密形成数字签名。具体的加密算法不作深究,比如:将hash值和private key进行异或操作。
- 把用户的Certificate (treat as a ID)和数字签名(treat as password)连同文档一块发出。
服务器端
- 根据文档的Certificate找到用户对应的public key。
- 使用public key来解密hash value, 得到原始hash值。对应上文:将hash值和public key进行异或操作(假定是shared secret key, 公钥和私钥相同)
- 使用相同的hash function计算生成文档的hash值。对应上文:取模hash
- 将两个hash值比较。如果不同,则说明文档或者被篡改(data is changed),或者用户身份不对(public key is not right, so certificate is not right)
hash function
MD5 (Message-Digest algorithm 5):生成 128-bit (16-byte) hash value. 32个16进制数(4 bit一个hexadecimal number.)
SHA1 (Secure Hash Algorithm): 生成160-bit hash value.
signature method(加密方法)
HMAC(Hash-based Message Authentication Code)
RSA(Rivest, Shamir and Adleman ) is an algorithm for public-key cryptography
Some knowledge about sniffing the network
重放攻击:将合法的请求重新发给服务器,从而获取用户数据。
防止重放攻击:在request中加入nonce(number used once),一个随机的unique string。服务器端在响应一次请求后,如果第二次接到相同的相应,可认为这是个重放攻击,直接reject。
timstamp: server端要实现重放攻击,必须记录所有曾经的nonce值,超超大的数据库才行。为了降低server端维护nonce的成本,request加入时间戳。server端只记录一段时间内的nonce值(比如一个小时内),如果timestamp older 1小时,server端可直接reject。因为没有保存更老的nonce值,无法验证其unique。
The concept(Terminology) of oauth
client: An HTTP client, 第三方应用(曾经叫consumer)
server: An HTTP client,拥有资源或者响应授权请求(authenticated requests)的平台
protected resource:用户数据
resource owner:用户
credentials(证书):可理解为ID和Password。如同前文介绍,oauth使用数字签名的一套,所以不完全等同。
- client credentials: consumer key/secret (nearly same with ID/password)
- temporary credentials: the authorization request (request token/secret)
- token: access grant (access token/secret)
token:上面credentials中的token,表示用户对某个应用的授权。唯一标示符和一个相应的shared-secret来签名加密(验证身份)
理论上只要两种credentials即可,一个是client应用本身的key/secret来标识应用;另外一个是表示用户对应用的授权,用户同意第三方应用访问用户的数据后,由server端生成一组access token/secret。第三方应用使用该授权(唯一标识符+secret)请求用户的数据。oauth的目标在此体现:
The decoupling of the User’s username and password from the Token is one of the most fundamental aspects of the OAuth architecture.
token/secret 和 username/password的区别在哪?理论上我们也可以使用password来进行数字签名,避免密码的明文传输。使用token的好处是可以随时revoke,可以有不同的access control。比如同一个用户授权A访问folder1,授权B访问folder2,生成不同的token,互不影响。比如限制一个token的有效期为1小时,平台不可能要求用户每一小时更换一个密码。
顺延这个思路,consumer key/consumer sercet作为应用的唯一标识,欠缺一定的灵活性。引入request token/secret,增加更多的控制。比如同样可以为request token设置有效期,如果应用在1小时内仍没有使用该token取得任何用户的授权,则该request token 过期。在oauth2中request token变为refresh token,由server端在返回access token时一块返回,简化了流程。
The process of oauth
From the user point (user channel)
从用户的角度看,过程如下:
- 访问某个应用
- 被重定向到平台,提示yes or no授权给应用。
- 点击yes(如果没有登录平台,则需输入用户名和密码),被重定向回应用页面
- 应用页面显示用户的数据
Backgroud channel
step1, 在用户访问应用页面时,client get request token from server。Authorization header的内容如下,不考虑验证部分,consumer_key和callback为输入,request_token/secret为输出
Request
POST /request_temp_credentials HTTP/1.1 Host: server.example.com Authorization: OAuth realm="Example", oauth_consumer_key="jd83jd92dhsh93js", oauth_signature_method="PLAINTEXT", oauth_callback="http%3A%2F%2Fclient.example.net%2Fcb%3Fx%3D1", oauth_signature="ja893SD9%26"
Response
HTTP/1.1 200 OK Content-Type: application/x-www-form-urlencoded oauth_token=hdk48Djdsa&oauth_token_secret=xyz4992k83j47x0b& oauth_callback_confirmed=true
step2, 用户被重定向到平台授权页面。请求输入为第一步中返回的request_token, 输出为verifier(request_toekn原样返回)
Request (重定向到平台)
GET /authorize_access?oauth_token=hdk48Djdsa HTTP/1.1 Host: server.example.com
Response(重定向返回应用)
GET /cb?x=1&oauth_token=hdk48Djdsa&oauth_verifier=473f82d3 HTTP/1.1 Host: client.example.net
setp3, client使用consumer_key, request_token, verifier来换取access_token
Request
POST /request_token HTTP/1.1 Host: server.example.com Authorization: OAuth realm="Example", oauth_consumer_key="jd83jd92dhsh93js", oauth_token="hdk48Djdsa", oauth_signature_method="PLAINTEXT", oauth_verifier="473f82d3", oauth_signature="ja893SD9%26xyz4992k83j47x0
Response
HTTP/1.1 200 OK Content-Type: application/x-www-form-urlencoded oauth_token=j49ddk933skd9dks&oauth_token_secret=ll399dj47dskfjd接下应用使用consumer_key, access_token来取得用户数据,这时需要加入nonce和timestamp防止重放攻击。
POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Authorization: OAuth realm="Example", oauth_consumer_key="9djdj82h48djs9d2", oauth_token="kkk9d7dh3k39sjv7", oauth_signature_method="HMAC-SHA1", oauth_timestamp="137131201", oauth_nonce="7d8f3e4a", oauth_signature="bYT5CMsGcbgUdFHObYMEfcx6bsw%3D" c2&a3=2+qThe sign of oauth
上面3步,其签名过程都一致(所有的Authorization header中都加入了signature_method和signature), 数字签名用的key是(HMAC-SHA1:consumer_secret&token_secret).
签名的内容(base string)如下。spec中详细定义了签名的base string内容和编码,
其中parameter部分包括所有The OAuth HTTP "Authorization" header field(除了oauth_signature)
1. The HTTP request method in uppercase. For example: "HEAD", "GET", "POST", etc. If the request uses a custom HTTP method, it MUST be encoded (Section 3.6). 2. An "&" character (ASCII code 38). 3. The base string URI from Section 3.4.1.2, after being encoded (Section 3.6). 4. An "&" character (ASCII code 38). 5. The request parameters as normalized in Section 3.4.1.3.2, after being encoded (Section 3.6).Future topic
上述过程只讨论了client端的流程,server端的实现可参见:Client/Server code
https://github.com/leah/python-oauth/
https://github.com/brosner/python-oauth2
DB Design
Reference
beginners guide: beginners-guide-to-oauth-part-iii-security-architecture
guide: http://hueniverse.com/oauth/guide/intro/
oauth1.0 spec: rfc5849 spec