引子
網上現在介紹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+q
The 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