什么是三方授權?
第三方授權就是,委托第三方來對既定的用戶進行鑒定,鑒定成功之后,下發信任憑證,信任憑證和用戶掛鉤,同時可以使用此憑證來去第三方平臺,獲得該用戶開放的部分信息.
直白的說,就是將用戶授權的工作交給第三方來做,而自己只維護信任憑證,并且獲取用戶信息。就像我們的QQ和微博一樣,你在逛各大論壇的時候,總會有一種途徑就是第三方的App登陸。顯然這種驗證是要在第三方做的,而論壇只需要維護一個token,并且可以獲取你的個人信息,然而沒什么軟用。你如果想用部分功能,還是得注冊,因為你相當于論壇只是一個有部分信息的游客,而沒有成為它的用戶,這種手段可以看成是提高自己訪問量或轉化率的一種手段吧。其實這種授權方案不一樣只用在互聯網,部分企業級的網站做資源的共享訪問也是這么做的。比如兩個網站合作,A站點賬戶可以登陸B站點,B站點賬戶可以登陸A站點,這樣A站點就能拿到B站點相關資源,反過來也是。就做到了資源共享。
那么這種授權是怎么做的呢?到底安全不安全?那么可能就要引出OAuth協議了。
OAuth是怎么做的#####
首先OAuth只是一個授權協議,不是一個實現或是一個中間件。
OAuth1于2007年10月建立,之后又在2009年6月重新修訂和發布 http://oauth.net/core/1.0a/
OAuth1.0授權流程是什么?
流程很復雜,因為OAuth需要保證授權碼和Token在傳輸的時候不被截取和篡改,所以使用了很多簽名反復的驗證。
OAuth1.0的問題:
反復的簽名,開放平臺本來就面對開發者,但是反復的簽名導致開發的流程太過于復雜。
沒有引入回跳地址的判斷,導致會跳可能會篡改,這樣授權碼和Token會被Hack掉(1.0a的時候修復了這個漏洞)。
授權流程過于單一化。
OAuth2.0的引入:
抽象驗證流程
由于OAuth1過于復雜,之后對OAuth進行了改造引入了Oauth2.0。OAuth的授權過程如下:
(A) 客戶端從資源擁有者那里請求授權。授權請求能夠直接發送給資源擁有者,或者間接地通過授權服務器這樣的中介,而后者更為可取。
(B) 客戶端收到一個訪問許可,它代表由資源服務器提供的授權。
(C) 客戶端使用它自己的私有證書到授權服務器上驗證,并出示訪問許可,來請求一個訪問令牌。
(D) 授權服務器驗證客戶端私有證書和訪問許可的有效性,如果驗證通過則分發一個訪問令牌。
(E) 客戶端通過出示訪問令牌向資源服務器請求受保護資源。
(F) 資源服務器驗證訪問令牌的有效性,如果驗證通過則響應這個資源請求。
上面只是一個抽象的驗證流程,根據上面的抽象流程演化出四種驗證模式:
授權碼模式
授權碼模式和上述的 抽象驗證模式流程比較相似:
(A) web客戶端通過將終端用戶的user-agent重定向到授權服務器來發起這個流程。客戶端傳入它的客戶端標識符、請求作用域、本地狀態和一個重定向URI,在訪問被許可(或被拒絕)后授權服務器會重新將終端用戶引導回這個URI。
(B) 授權服務器驗證終端用戶(借助于user-agent),并確定終端用戶是否許可客戶端的訪問請求。
(C) 假定終端用戶許可了這次訪問,授權服務器會將user-agent重定向到之前提供的重定向URI上去。授權服務器為客戶端傳回一個授權碼做獲取訪問令牌之用。
(D) 客戶端通過驗證并傳入上一步取得的授權碼從授權服務器請求一個訪問令牌。(需要帶上ClientId和Secret,ClientId和Secret是通過平臺授予)
(E) 授權服務器驗證客戶端私有證書和授權碼的有效性并返回訪問令牌。
授權碼模式(implicit grant type)
User-Agent子態適用于客戶端不能保存客戶端私有證書的App(純客戶端App,無Server參與)。因為可純客戶端的程序不能保存密鑰
(A) 客戶端將user-agent引導到終端用戶授權endpoint。客戶端傳入它的客戶端標識符、請求作用域、本地狀態和一個重定向URI,在訪問被許可(或被拒絕)后授權服務器會重新將終端用戶引導回這個URI。
(B) 授權服務器驗證終端用戶(通過user-agent)并確認終端用戶是許可還是拒絕了客戶端的訪問請求。
(C) 如果終端用戶許可了這次訪問,那么授權服務器會將user-agent引導到之前提供的重定向URI。重定向URI會在URI片斷{譯者注:URI片斷是指URI中#號之后的內容}中包含訪問令牌。
(D) user-agent響應重定向指令,向web服務器發送不包含URI片斷的請求。user-agent在本地保存URI片斷。
(E) web服務器返回一個web頁面(通常是嵌入了腳本的HTML網頁),這個頁面能夠訪問完整的重定向URI,它包含了由user-agent保存的URI片斷,同時這個頁面能夠將包含在URI片斷中的訪問令牌(和其它參數)提取出來。
(F) user-agent在本地執行由web服務器提供的腳本,該腳本提取出訪問令牌并將它傳遞給客戶端。
密碼模式
密碼模式就是將密碼托管給第三方App,但是必須要保證第三方App高度可信。
(A)用戶向客戶端提供用戶名和密碼。
(B)客戶端將用戶名和密碼發給認證服務器,向后者請求令牌。
(C)認證服務器確認無誤后,向客戶端提供訪問令牌。
客戶端模式
這種模式不需要終端用戶的參與,只是Client和Server端的交互。通常只用于Client狀態的獲取。
(A)客戶端向認證服務器進行身份認證,并要求一個訪問令牌。
(B)認證服務器確認無誤后,向客戶端提供訪問令牌。
授權流程
這里以授權碼方式流程說明,主要流程分為兩步,獲取授權碼和通過授權碼獲取資源票據:
(A)用戶訪問客戶端,后者將前者導向認證服務器。
(B)用戶選擇是否給予客戶端授權。
(C)假設用戶給予授權,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個授權碼。
(D)客戶端收到授權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的后臺的服務器上完成的,對用戶不可見。
(E)認證服務器核對了授權碼和重定向URI,確認無誤后,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)。
A步驟中,客戶端申請認證的URI,包含以下參數:
response_type:表示授權類型,必選項,此處的值固定為"code"
client_id:表示客戶端的ID,必選項
redirect_uri:表示重定向URI,可選項
scope:表示申請的權限范圍,可選項
state:表示客戶端的當前狀態,可以指定任意值,認證服務器會原封不動地返回這個值。
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
C步驟中,服務器回應客戶端的URI,包含以下參數:
code:表示授權碼,必選項。該碼的有效期應該很短,通常設為10分鐘,客戶端只能使用該碼一次,否則會被授權服務器拒絕。該碼與客戶端ID和重定向URI,是一一對應關系。
state:如果客戶端的請求中包含這個參數,認證服務器的回應也必須一模一樣包含這個參數。
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
D步驟中,客戶端向認證服務器申請令牌的HTTP請求,包含以下參數:
grant_type:表示使用的授權模式,必選項,此處的值固定為"authorization_code"。
code:表示上一步獲得的授權碼,必選項。
redirect_uri:表示重定向URI,必選項,且必須與A步驟中的該參數值保持一致。
client_id:表示客戶端ID,必選項。
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
E步驟中,認證服務器發送的HTTP回復,包含以下參數:
access_token:表示訪問令牌,必選項。
token_type:表示令牌類型,該值大小寫不敏感,必選項,可以是bearer類型或mac類型。
expires_in:表示過期時間,單位為秒。如果省略該參數,必須其他方式設置過期時間。
refresh_token:表示更新令牌,用來獲取下一次的訪問令牌,可選項。
scope:表示權限范圍,如果與客戶端申請的范圍一致,此項可省略。
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
資源訪問過程:
http://localhost:8082/oauth/server/resource/uname.do?access_token=233730a3d4cdc1af64f78fa80bc13c6e
此處主要由資源服務方實現,對于access_token進行校驗。
token過期刷新過程:
客戶端使用“refresh_token”訪問許可類型和下列參數傳入刷新令牌:
refresh_token:
必需參數。與待刷新的訪問令牌相關聯的刷新令牌。
grant_type=refresh_token&client_id=s6BhdRkqt3&client_secret=8eSEIpnqmM&refresh_token=n4E9O119d
授權服務器必須驗證客戶端私有證書(如果存在),驗證刷新令牌是否有效,以及驗證資源擁有者的授權是否仍然有效。如果請求有效,授權服務器則發布一個訪問令牌響應,原來的Token失效。以后訪問必須使用新Token。
一切只是看上去很美好,但其實很多坑####
OAuth2看上去很美好,但是細心觀察其實還是有一些漏洞的。
對于授權碼和access_token的篡改,在OAuth1中是反復的對Code和Token進行簽名,來保證Token不會被篡改,但是OAuth2中卻沒有,因為OAuth2是基于Https的,所以如果沒有Https的支持OAuth2可能還不如OAuth1.
對于redirect_uri的校驗,OAuth1中沒有提到redirect_uri的校驗,那么OAuth2中要求進行redirect_uri的校驗。但是如果校驗規則過松,也會導致跳轉的安全問題。 例如:校驗的時候只校驗根域名,或者二級域名,但是第三方App對自己的域名保護的不好,導致二級域名被hack那么此時授權碼和Token會被竊取。 校驗規則不嚴謹,例如www.baidu.com 但是redirect_uri為:www.a.com.\www.baidu.com,這樣授權就被a.com釣走了。
對于CSRF攻擊(跨站請求偽造):由于這個授權過程服務器和Client和用戶之間有幾次交互,但是在得到授權碼的時候需要一次回跳,但是這次回跳是可以被阻塞的。
攻擊者使用自己的賬戶申請第三方授權登陸
授權后服務端返回授權碼,但是此時組織授權回跳,此時Client并沒有接到授權碼,也就是阻斷了授權流程
攻擊者將此跳轉鏈接發給一個正在處于在Client登陸狀態的賬戶
誘騙正常用戶點擊,那么此時攻擊者第三方賬戶和被攻擊賬戶進行綁定(相當于賬戶綁定了第三方的賬戶)
攻擊者再次進行第三方授權登陸。這樣就劫持了誘騙的賬戶。轉賬?刪好友?等等就所以搞了。
解決辦法:
在進行授權碼申請或者是Token申請的時候帶上state參數,服務器返回請求時要求攜帶state參數,在Client處理授權的時候校驗此參數。參數可以是當前賬戶的SessionId,或Cookie的簽名串。在Client申請accessToken會驗證相關state和當前用戶的關系,這樣就防止了篡改。OAuth的校驗流程為什么這么復雜,直接授權之后redirect回accessToken不就結了嗎?為什么還要返回auth_code之后請求accessToken?
首先,redirect是不安全的,隨時可以暫停回調而拿到accessToken,拿到了accessToken也就意味著拿到了授權,但是auth_code是和client相對應的,那么即使拿到了auth_code還需要再次申請accessToken,申請accessToken時需要校驗Client和state。同時協議設計的原則就是只有Client能拿到accessToken而用戶是拿不到的。
最后,切記HTTPS####
參考資料:
http://blog.csdn.net/seccloud/article/details/8192707
http://www.justwinit.cn/post/8138/
https://blog.yorkxin.org/2013/09/30/oauth2-1-introduction
人人網OAuth歷程:http://www.infoq.com/cn/presentations/dx-renren-authentication-authorization/