OAuth2.0協(xié)議原理詳解

OAuth 2.0 是目前比較流行的做法,它率先被Google, Yahoo, Microsoft, Facebook等使用。之所以標(biāo)注為 2.0,是因?yàn)樽畛跤幸粋€(gè)1.0協(xié)議,但這個(gè)1.0協(xié)議被弄得太復(fù)雜,易用性差,所以沒有得到普及。2.0是一個(gè)新的設(shè)計(jì),協(xié)議簡單清晰,但它并不兼容1.0,可以說與1.0沒什么關(guān)系。所以,我就只介紹2.0。

協(xié)議的參與者

從引言部分的描述我們可以看出,OAuth的參與實(shí)體至少有如下4個(gè):

  • RO (resource owner): 資源所有者,對(duì)資源具有授權(quán)能力的人。如上文中的用戶Alice。
  • RS (resource server): 資源服務(wù)器,它存儲(chǔ)資源,并處理對(duì)資源的訪問請(qǐng)求。如Google資源服務(wù)器,它所保管的資源就是用戶Alice的照片。
  • Client: 第三方應(yīng)用,它獲得RO的授權(quán)后便可以去訪問RO的資源。如網(wǎng)易印像服務(wù)。
  • AS (authorization server): 授權(quán)服務(wù)器,它認(rèn)證RO的身份,為RO提供授權(quán)審批流程,并最終頒發(fā)授權(quán)令牌(Access Token)。讀者請(qǐng)注意,為了便于協(xié)議的描述,這里只是在邏輯上把AS與RS區(qū)分開來;在物理上,AS與RS的功能可以由同一個(gè)服務(wù)器來提供服務(wù)。

授權(quán)類型

在開放授權(quán)中,第三方應(yīng)用(Client)可能是一個(gè)Web站點(diǎn),也可能是在瀏覽器中運(yùn)行的一段JavaScript代碼,還可能是安裝在本地的一個(gè)應(yīng)用程序。這些第三方應(yīng)用都有各自的安全特性。對(duì)于Web站點(diǎn)來說,它與RO瀏覽器是分離的,它可以自己保存協(xié)議中的敏感數(shù)據(jù),這些密鑰可以不暴露給RO;對(duì)于javascript代碼和本地安全的應(yīng)用程序來說,它本來就運(yùn)行在RO的瀏覽器中,RO是可以訪問到Client在協(xié)議中的敏感數(shù)據(jù)。

OAuth為了支持這些不同類型的第三方應(yīng)用,提出了多種授權(quán)類型,如:

  • 授權(quán)碼 (Authorization Code Grant)
  • 隱式授權(quán) (Implicit Grant)
  • RO憑證授權(quán) (Resource Owner Password Credentials Grant)
  • Client憑證授權(quán) (Client Credentials Grant)

由于本文旨在幫助用戶理解OAuth協(xié)議,所以我將先介紹這些授權(quán)類型的基本思路,然后選擇其中最核心、最難理解、也是最廣泛使用的一種授權(quán)類型——“授權(quán)碼”,進(jìn)行深入的介紹。

2.1 授權(quán)碼模式
mark

(A) web客戶端通過將終端用戶的user-agent重定向到授權(quán)服務(wù)器來發(fā)起這個(gè)流程。客戶端傳入它的客戶端標(biāo)識(shí)符、請(qǐng)求作用域、本地狀態(tài)和一個(gè)重定向URI,在訪問被許可(或被拒絕)后授權(quán)服務(wù)器會(huì)重新將終端用戶引導(dǎo)回這個(gè)URI。

(B) 授權(quán)服務(wù)器驗(yàn)證終端用戶(借助于user-agent),并確定終端用戶是否許可客戶端的訪問請(qǐng)求。

(C) 假定終端用戶許可了這次訪問,授權(quán)服務(wù)器會(huì)將user-agent重定向到之前提供的重定向URI上去。授權(quán)服務(wù)器為客戶端傳回一個(gè)授權(quán)碼做獲取訪問令牌之用。

(D) 客戶端通過驗(yàn)證并傳入上一步取得的授權(quán)碼從授權(quán)服務(wù)器請(qǐng)求一個(gè)訪問令牌。(需要帶上ClientId和Secret,ClientId和Secret是通過平臺(tái)授予)

(E) 授權(quán)服務(wù)器驗(yàn)證客戶端私有證書和授權(quán)碼的有效性并返回訪問令牌。

2.2 隱授權(quán)模式(implicit grant type)

User-Agent適用于客戶端不能保存客戶端私有證書的App(純客戶端App,無Server參與)。因?yàn)榧兛蛻舳说某绦虿荒鼙4婷荑€

mark

(A) 客戶端將user-agent引導(dǎo)到終端用戶授權(quán)endpoint。客戶端傳入它的客戶端標(biāo)識(shí)符、請(qǐng)求作用域、本地狀態(tài)和一個(gè)重定向URI,在訪問被許可(或被拒絕)后授權(quán)服務(wù)器會(huì)重新將終端用戶引導(dǎo)回這個(gè)URI。

(B) 授權(quán)服務(wù)器驗(yàn)證終端用戶(通過user-agent)并確認(rèn)終端用戶是許可還是拒絕了客戶端的訪問請(qǐng)求。

(C) 如果終端用戶許可了這次訪問,那么授權(quán)服務(wù)器會(huì)將user-agent引導(dǎo)到之前提供的重定向URI。重定向URI會(huì)在URI片斷{譯者注:URI片斷是指URI中#號(hào)之后的內(nèi)容}中包含訪問令牌。

(D) user-agent響應(yīng)重定向指令,向web服務(wù)器發(fā)送不包含URI片斷的請(qǐng)求。user-agent在本地保存URI片斷。

(E) web服務(wù)器返回一個(gè)web頁面(通常是嵌入了腳本的HTML網(wǎng)頁),這個(gè)頁面能夠訪問完整的重定向URI,它包含了由user-agent保存的URI片斷,同時(shí)這個(gè)頁面能夠?qū)赨RI片斷中的訪問令牌(和其它參數(shù))提取出來。

(F) user-agent在本地執(zhí)行由web服務(wù)器提供的腳本,該腳本提取出訪問令牌并將它傳遞給客戶端。

2.3 密碼模式

密碼模式就是將密碼托管給第三方App,但是必須要保證第三方App高度可信。

mark

(A)用戶向客戶端提供用戶名和密碼。

(B)客戶端將用戶名和密碼發(fā)給認(rèn)證服務(wù)器,向后者請(qǐng)求令牌。

(C)認(rèn)證服務(wù)器確認(rèn)無誤后,向客戶端提供訪問令牌。

2.4 客戶端模式

這種模式不需要終端用戶的參與,只是Client和Server端的交互。通常只用于Client狀態(tài)的獲取。

mark

(A)客戶端向認(rèn)證服務(wù)器進(jìn)行身份認(rèn)證,并要求一個(gè)訪問令牌。

(B)認(rèn)證服務(wù)器確認(rèn)無誤后,向客戶端提供訪問令牌。

授權(quán)碼模式的實(shí)例化描述

面我以實(shí)例化方式來幫助讀者理解授權(quán)碼類型的授權(quán)協(xié)議的運(yùn)行過程。假設(shè):

(1) Alice有一個(gè)有效的Google帳號(hào);

(2) Facebook.com已經(jīng)在Google Authorization Server上注冊(cè)了Client身份,已經(jīng)獲得(client_id, client_secret),注意client_secret是Client與AS之間的一個(gè)共享密鑰。

(3) Alice想授權(quán)Facebook.com查看她的聯(lián)系人列表(https://www.google.com/m8/feeds)。

下圖展示了Alice、Facebook.com、Google資源服務(wù)器、以及Google OAuth授權(quán)服務(wù)器之間的協(xié)議運(yùn)行過程。


mark

協(xié)議所涉及到的細(xì)節(jié)都已經(jīng)在圖上了,所以不打算再做詳細(xì)介紹了。若看懂了此圖,OAuth2.0就理解了。

讀者請(qǐng)注意,在步驟(4)中,Client需要拿“授權(quán)碼”去換“授權(quán)令牌”時(shí),Client需要向AS證明自己的身份,即證明自己就是步驟(2)中Alice批準(zhǔn)授權(quán)時(shí)的Grantee。這個(gè)身份證明的方法主要有兩種(圖3中使用了第1種):

(1) 通過https直接將client_secret發(fā)送給AS,因?yàn)閏lient_secret是由Client與AS所共享,所以只要傳送client_secret的信道安全即可。

(2) 通過消息認(rèn)證碼來認(rèn)證Client身份,典型的算法有HMAC-SHA1。在這種方式下,Client無需傳送client_secret,只需發(fā)送消息請(qǐng)求的signature即可。由于不需要向AS傳遞敏感數(shù)據(jù),所以它只需要使用http即可。

此外, 在步驟(2)中,Google授權(quán)服務(wù)器需要認(rèn)證Alice的RO身份,并提供授權(quán)界面給Alice進(jìn)行授權(quán)審批。今天Google提供的實(shí)例如下圖所示,僅供讀者理解OAuth這種“現(xiàn)場授權(quán)”或"在線授權(quán)"的含義。


mark
mark

OAuth2.0授權(quán)類型選擇

授權(quán)模式實(shí)際上就是指獲取token的方法,選擇授權(quán)模式主要取決于最終用戶使用的客戶端的類型,以及你服務(wù)對(duì)用戶的表現(xiàn)形式。

具體的選擇方式如下圖:

mark
First Party or third party client?

First party指的是,你完全信任這個(gè)客戶端可以管理好最終用戶的認(rèn)證憑據(jù)。比如說對(duì)于Spotify的開發(fā)者和所有者來說,他們完全信任他們的Spotify iphone客戶端。而third party客戶端是指我們不信任的客戶端。

Access Token Owner?

Access Token表示給一個(gè)客戶端授權(quán)訪問某些被保護(hù)的資源。如果我們只需要給一個(gè)機(jī)器授權(quán)訪問某些資源,而不需要給人授權(quán),那么這些資源應(yīng)該實(shí)現(xiàn)client credential grant。

如果我們需要給人授權(quán)訪問資源,那么需要視客戶端的類型做決定。

Client type?

客戶端類型主要是指客戶端是否有能力保存秘鑰。

如果客戶端是個(gè)web app,并且有服務(wù)器側(cè)的組件,那么我們應(yīng)該選擇authorization code grant

如果客戶端是一個(gè)純前端的web app(如:單頁面應(yīng)用),如果客戶端可信,我們可以選擇password grant,如果客戶端不可信可以選擇implicit grant

如果客戶端是一個(gè)本地應(yīng)用,比如一個(gè)手機(jī)app,應(yīng)該選擇password grant。

第三方本地應(yīng)用應(yīng)該用authorization code grant(通過本地瀏覽器,不要用嵌入式瀏覽器,比如iOS強(qiáng)制用戶用Safari或者SFSafariViewController,而不用 WKWebView)。

OAuth設(shè)計(jì)上的安全性考慮

為何引入authorization_code?

協(xié)議設(shè)計(jì)中,為什么要使用authorization_code來交換access_token?這是讀者容易想到的一個(gè)問題。也就是說,在協(xié)議的第3步,為什么不直接將access_token通過重定向方式返回給Client呢?比如:

HTTP/1.1 302

Location:

https://www.facebook.com/?access_token=ya29.AHES6ZSXVKYTW2VAGZtnMjD&token_type=Bearer&expires_in=3600

如果直接返回access_token,協(xié)議將變得更加簡潔,而且少一次Client與AS之間的交互,性能也更優(yōu)。那為何不這么設(shè)計(jì)呢?協(xié)議文檔[1]中并沒有給出這樣設(shè)計(jì)的理由,但也不難分析:

(1) 瀏覽器的redirect_uri是一個(gè)不安全信道,此方式不適合于傳遞敏感數(shù)據(jù)(如access_token)。因?yàn)閡ri可能通過HTTP referrer被傳遞給其它惡意站點(diǎn),也可能存在于瀏覽器cacher或log文件中,這就給攻擊者盜取access_token帶來了很多機(jī)會(huì)。另外,此協(xié)議也不應(yīng)該假設(shè)RO用戶代理的行為是可信賴的,因?yàn)镽O的瀏覽器可能早已被攻擊者植入了跨站腳本用來監(jiān)聽access_token。因此,access_token通過RO的用戶代理傳遞給Client,會(huì)顯著擴(kuò)大access_token被泄露的風(fēng)險(xiǎn)。 但authorization_code可以通過redirect_uri方式來傳遞,是因?yàn)閍uthorization_code并不像access_token一樣敏感。即使authorization_code被泄露,攻擊者也無法直接拿到access_token,因?yàn)槟胊uthorization_code去交換access_token是需要驗(yàn)證Client的真實(shí)身份。也就是說,除了Client之外,其他人拿authorization_code是沒有用的。 此外,access_token應(yīng)該只頒發(fā)給Client使用,其他任何主體(包括RO)都不應(yīng)該獲取access_token。協(xié)議的設(shè)計(jì)應(yīng)能保證Client是唯一有能力獲取access_token的主體。引入authorization_code之后,便可以保證Client是access_token的唯一持有人。當(dāng)然,Client也是唯一的有義務(wù)需要保護(hù)access_token不被泄露。

(2) 引入authorization_code還會(huì)帶來如下的好處。由于協(xié)議需要驗(yàn)證Client的身份,如果不引入authorization_code,這個(gè)Client的身份認(rèn)證只能通過第1步的redirect_uri來傳遞。同樣由于redirect_uri是一個(gè)不安全信道,這就額外要求Client必須使用數(shù)字簽名技術(shù)

來進(jìn)行身份認(rèn)證,而不能用簡單的密碼或口令認(rèn)證方式。引入authorization_code之后,AS可以直接對(duì)Client進(jìn)行身份認(rèn)證(見步驟4和5),而且可以支持任意的Client認(rèn)證方式(比如,簡單地直接將Client端密鑰發(fā)送給AS)。

在我們理解了上述安全性考慮之后,讀者也許會(huì)有豁然開朗的感覺,懂得了引入authorization_code的妙處。那么,是不是一定要引入authorization_code才能解決這些安全問題呢?當(dāng)然不是。筆者將會(huì)在另一篇博文給出一個(gè)直接返回access_token的擴(kuò)展授權(quán)類型解決方案,它在滿足相同安全性的條件下,使協(xié)議更簡潔,交互次數(shù)更少。

一切只是看上去很美好,但其實(shí)很多坑

OAuth2看上去很美好,但是細(xì)心觀察其實(shí)還是有一些漏洞的。

對(duì)于授權(quán)碼和access_token的篡改,在OAuth1中是反復(fù)的對(duì)Code和Token進(jìn)行簽名,來保證Token不會(huì)被篡改,但是OAuth2中卻沒有,因?yàn)镺Auth2是基于Https的,所以如果沒有Https的支持OAuth2可能還不如OAuth1.

對(duì)于redirect_uri的校驗(yàn),OAuth1中沒有提到redirect_uri的校驗(yàn),那么OAuth2中要求進(jìn)行redirect_uri的校驗(yàn)。但是如果校驗(yàn)規(guī)則過松,也會(huì)導(dǎo)致跳轉(zhuǎn)的安全問題。 例如:校驗(yàn)的時(shí)候只校驗(yàn)根域名,或者二級(jí)域名,但是第三方App對(duì)自己的域名保護(hù)的不好,導(dǎo)致二級(jí)域名被hack那么此時(shí)授權(quán)碼和Token會(huì)被竊取。 校驗(yàn)規(guī)則不嚴(yán)謹(jǐn),例如www.baidu.com 但是redirect_uri為:www.a.com.\www.baidu.com,這樣授權(quán)就被a.com釣走了。

對(duì)于CSRF攻擊(跨站請(qǐng)求偽造):由于這個(gè)授權(quán)過程服務(wù)器和Client和用戶之間有幾次交互,但是在得到授權(quán)碼的時(shí)候需要一次回跳,但是這次回跳是可以被阻塞的。

攻擊者使用自己的賬戶申請(qǐng)第三方授權(quán)登陸

授權(quán)后服務(wù)端返回授權(quán)碼,但是此時(shí)組織授權(quán)回跳,此時(shí)Client并沒有接到授權(quán)碼,也就是阻斷了授權(quán)流程

攻擊者將此跳轉(zhuǎn)鏈接發(fā)給一個(gè)正在處于在Client登陸狀態(tài)的賬戶

誘騙正常用戶點(diǎn)擊,那么此時(shí)攻擊者第三方賬戶和被攻擊賬戶進(jìn)行綁定(相當(dāng)于賬戶綁定了第三方的賬戶)

攻擊者再次進(jìn)行第三方授權(quán)登陸。這樣就劫持了誘騙的賬戶。轉(zhuǎn)賬?刪好友?等等就所以搞了。

解決辦法:

在進(jìn)行授權(quán)碼申請(qǐng)或者是Token申請(qǐng)的時(shí)候帶上state參數(shù),服務(wù)器返回請(qǐng)求時(shí)要求攜帶state參數(shù),在Client處理授權(quán)的時(shí)候校驗(yàn)此參數(shù)。參數(shù)可以是當(dāng)前賬戶的SessionId,或Cookie的簽名串。在Client申請(qǐng)accessToken會(huì)驗(yàn)證相關(guān)state和當(dāng)前用戶的關(guān)系,這樣就防止了篡改。

OAuth的校驗(yàn)流程為什么這么復(fù)雜,直接授權(quán)之后redirect回accessToken不就結(jié)了嗎?為什么還要返回auth_code之后請(qǐng)求accessToken?

首先,redirect是不安全的,隨時(shí)可以暫停回調(diào)而拿到accessToken,拿到了accessToken也就意味著拿到了授權(quán),但是auth_code是和client相對(duì)應(yīng)的,那么即使拿到了auth_code還需要再次申請(qǐng)accessToken,申請(qǐng)accessToken時(shí)需要校驗(yàn)Client和state。同時(shí)協(xié)議設(shè)計(jì)的原則就是只有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
人人網(wǎng)OAuth歷程:http://www.infoq.com/cn/presentations/dx-renren-authentication-authorization/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容