hi,這里是桑小榆,這次分享的不是生活文,而是技術文。
基于OAuth2.0協議的授權認證,初次接觸授權認證知識的時候,出現了不少熱門名詞,“OAuth”,“JWT”,“OIDC”,這簡直讓人頭大,暈頭轉向甚至學了又不知道自己學了啥,網絡上看見相關的文章也是一頭霧水,要么是部分知識,要么是講著講著莫名其妙蹦出一個陌生詞。
▲圖/?來源《功夫》
所以這次,本著大道至簡的思想,和大家一起探討,如果有看不懂的地方,那就是我沒理解或者講明白。本篇不包含源碼,先以理論基礎理解作為鋪墊,后篇將會以源碼實操的方式進行幫助理解。
首先我們回顧傳統的授權方式,基本是通過賬號,密碼的方式進行授權認證,授予第三方也是通過共享密碼的方式進行授權。隨著信息化的普及,系統越來越龐大,人人對于網絡觸手可及,這就容易帶來安全隱患。
第一,用戶授權第三方通過共享密碼,此處密碼也是明文的,顯然很不安全。
第二,我們給第三方的權限其實也就一小部分,如果通過密碼共享,則第三方通過你的密碼可以獲取所有的權限,這是不安全的。
第三,我們授權了第三方應用之后,我們是無法撤回權限的。只能通過更改密碼,那么我們授權的第三方都會受影響,非常麻煩且需要重新授權。
基于以上傳統授權方式的缺陷,于是引入了OAuth協議。協議,顧名思義就是大家(國際互聯組織)共同達成的一種標準的規則。這種協議,例如大家耳熟能詳的IP協議,TCP協議等都是共同制定的一種標準協議,防止各個國家或企業擁有自己的協議就容易造成協議泛濫,使用受限且對接繁雜。
OAuth協議,也屬于網絡層的協議,為了保護資源安全而存在。既然是協議,那必然需要一種傳輸介質,使得挨家挨戶都認識,且需要攜帶能夠識別身份的信息,那就是JWT(Json Web Token),也就是我們常說的Token令牌,關于JWT內容將在下篇探討。
那么,OAuth協議包含了哪些內容呢?
首先是四個主要的參與角色。
Resource Owner:資源所有者,對資源具有授權能力的人,也就是用戶。
Resource Server: 資源服務器,保存用戶信息的服務器并且能夠驗證令牌是否合法(比如說微信服務器,保存你的微信頭像,昵稱等)。
Client:客戶端,第三方應用,它獲得用戶的授權后便可以去訪問用戶的信息。比如嗶哩嗶哩。
Authorization Server:授權服務器,它將認證用戶的身份,為用戶提供授權審批流程,并最終頒發授權令牌(Access Token)。發送令牌給第三方的服務器(可以和資源服務器是同一個)
通過OAuth協議的幾個參與者,我們可以總結OAuth的作用,是讓用戶的權限,安全可控的授予第三方應用,第三方應用獲取到用戶授予的權限之后,與資源服務器進行交互。
其次,OAuth 支持四種授權模式。
由于客戶端必須得到用戶的授權(authorization grant),才能獲得令牌(access token)。因此OAuth支持獲得令牌的方式有四種。
授權碼模式(authorization code)
授權碼模式是比較標準的模式,微信、GitHub、嗶哩嗶哩等知名應用就是使用的這種模式,因為通過授權碼的方式可以有效的隱藏令牌不被泄露,安全性更高。
具體授權碼如何操作,我們可以舉個實際的例子:
▲圖/?來源B站登錄界面
比如,我們想登錄嗶哩嗶哩網站,嗶哩嗶哩提供了第三方登錄方式,微信,微博或者QQ登錄。
1.當我們選擇微信登錄的時候,嗶哩嗶哩會提供一個跳轉鏈接,我們點擊的時會跳轉到微信授權服務器。
//接口服務
https://open.weixin.qq.com/connect/qrconnect
?appid=wxafc256bf83583323
&redirect_uri=https%3A%2F%2Fpassport.bilibili.com%2Flogin%2Fsnsback%3Fsns%3Dwechat%26state%3D8b90df300a6711edbeb2d280ef8fddbc%26source%3Dnew_main_mini
&response_type=code&scope=snsapi_login
&state=authorize#wechat_redirect參數解釋:
appid:是微信端開放的授權標識,相當于client_id。
refirect_uri:回跳地址,微信生成授權碼code之后就會在這個鏈接上回傳給嗶哩嗶哩。
response_type:授權類型,此處為授權碼類型。
scope:授權范圍,此處為是授權給嗶哩嗶哩的授權api。
state:校驗參數,校驗用戶信息是否被篡改。
2.我們掃碼之后,微信服務器會詢問我們是否授權嗶哩嗶哩(將獲取你的昵稱、頭像)。
3.當我們點擊允許的時候,微信授權服務器就會針對當前操作的用戶返回一個授權碼給代理用戶,代理用戶就是指的瀏覽器,因為是瀏覽器幫我們去詢問授權的。
4.確認授權之后,代理用戶(瀏覽器)就會把這個授權碼通過重定向地址傳回給嗶哩嗶哩。
5.嗶哩嗶哩拿著這個授權碼和其他重要認證信息向微信授權服務器請求令牌。
6.微信授權服務器接收并識別這個授權碼之后發送一個令牌給嗶哩嗶哩。
7.嗶哩嗶哩拿著這個令牌就去獲取微信資源服務器讀取用戶資料。
▲圖/?授權碼模式圖解
隱式模式(不推薦使用),適合沒有后臺的第三方。
他的使用方式,我們還通過嗶哩嗶哩登錄的例子說明。
//接口服務
https://open.weixin.qq.com/connect/qrconnect
?appid=wxafc256bf83583323
&redirect_uri=https://www.blibli.com/callback
&response_type=token&scope=snsapi_login
&state=authorize#wechat_redirect
1.登錄嗶哩嗶哩時,會提供一個微信登錄的鏈接,我們點擊之后會跳轉到微信授權服務器。
2.進入之后,微信服務器會詢問我們是否授權嗶哩嗶哩,并確認授權給嗶哩嗶哩。
3.此時,微信授權服務器直接把令牌發送給了嗶哩嗶哩,嗶哩嗶哩根據令牌獲取用戶的信息。
我們發現,這個模式是沒有嗶哩嗶哩后臺認證與微信授權服務交互的過程,僅僅是拿到令牌之后返回到了嗶哩嗶哩的前臺。這種方式我們很容易通過鏈接進行存儲和偽造授權,是不安全的。
密碼模式,適合傳統的賬戶密碼系統改造為OAuth授權,以及用戶在很信任第三方的情況下使用。
//接口服務
https://open.weixin.qq.com/connect/qrconnect
?appid=wxafc256bf83583323
&grant_type=password
&username=sunny_100kmiles&rpassword=imluckyboy
&scope=snsapi_login
&state=authorize //wechat_redirect
1.該模式登錄嗶哩嗶哩時,嗶哩嗶哩會使用我們的賬號密碼直接向微信授權服務器索要令牌。
2.微信授權服務器接收到賬號密碼之后匹配成功則會發送令牌給嗶哩嗶哩。
3.嗶哩嗶哩根據令牌獲取我們的信息,但如果更改了密碼之后也需要重新刷新令牌。
客戶端模式,適合沒有前端的第三方,也沒有用戶的參與。僅僅是授權服務器與資源服務器之間的交互。
//接口服務
https://open.weixin.qq.com/connect/qrconnect
?appid=wxafc256bf83583323
&grant_type=client_credentials
&client_secret=xxxxxxxx
綜合以上四個授權模式,有些敏銳的小伙伴會發現,第一種整體來看較為復雜,為何要使用授權碼code去微信授權服務器獲取token(令牌)呢?直接在第四步,將token直接返回給客戶端嗶哩嗶哩,不是更加方便嗎?還減少了一次客戶端與授權服務器的交互,性能上也更優?
這種授權碼的設計方式也不難理解,如果直接將token(令牌)通過redirect_uri回調的方式返回給客戶端嗶哩嗶哩的話,也就是經過了代理用戶(瀏覽器)這一層,此時瀏覽器傳送的過程中很容易存到瀏覽器的cacher和log記錄中,也容易傳到其他惡意站點或者被截獲,這給攻擊者盜取令牌帶來了更多機會。
并且瀏覽器的redirect_uri本身就是一個不安全的信息通道,通常我們不會把重要,敏感的數據以這種方式傳遞。
因此,引入授權碼進行授權,OAuth協議經用戶授權之后生成一個code給嗶哩嗶哩客戶端,嗶哩嗶哩后臺將會根據這個code和其他重要信息(例如微信的appid,appsecrect等)向微信授權服務獲取令牌。這個時候我們會發現就算授權碼code被攻擊者截獲了也起不到任何的作用,大大提高了安全性。
那么,這種模式設計就無懈可擊了?顯然拿到令牌之后,向資源服務器請求交互的過程中,還是會被截獲token,甚至被篡改。
在OAuth1.0協議中,是通過反復的對授權碼code和token進行簽名,來保證token不會被篡改,但是OAuth2.0卻沒有這項,因為OAuth2.0是基于https的,我們知道https協議傳送的報文是加密的,不容易被篡改。顯然OAuth2.0在這基礎之上,性能上更優于OAuth1.0的。
還有一種存在的問題就是。用戶授權之后,授權碼code會通過redirect_uri進行傳回給嗶哩嗶哩客戶端,如果沒有對這串redirect_uri進行校驗,或者校驗規則級別不高。例如,嗶哩嗶哩提供的回調地址是www.blibli.com,但是被人截獲篡改成了www.clicli.com\www.blibli.com,那么這樣的授權就被www.clicli給劫走了。這就是跨站請求偽造。
由于這個授權服務器,嗶哩嗶哩客戶端和用戶之間有幾次交互,在得到授權碼的時候需要一次回跳,但是這次回跳是可以被阻塞的。
那么就會出現這樣的案例,比如我是黑客,我使用自己的賬號登錄嗶哩嗶哩進行第三方微信登錄,當我授權之后,微信授權服務會返回帶有授權碼code的回跳鏈接給嗶哩嗶哩客戶端,返回的過程中被我阻塞了,嗶哩嗶哩客戶端就收不到授權碼。
此時,我將這個跳轉鏈接發給正處于登錄狀態的用戶(小張)。
誘導小張進行正常點擊,那么我的賬號返回的授權碼鏈接就被小張點擊之后向微信服務器獲取令牌,那么此時我的第三方賬號和小張的賬號就已經綁定了,此時我相當于以小張的賬號進行登錄嗶哩嗶哩,進行刪除資源,刪除好友,取關等一系列惡意操作。
藝術來源于生活,這一點兒也不假。在網絡上看到過這樣的一起案例。
A女士前往ATM取錢,在插卡輸入密碼之后,后面排隊的B先生扔了幾張真鈔到A女士的腳旁誘導她去撿真鈔,B先生遂即切換A女士的卡將自己的卡插入ATM機,之后A女士發現需要重新登錄,輸入幾次密碼之后依然輸入錯誤,慌忙之中B先生建議A女士前往旁邊的前臺咨詢,于是便去了。在A女士連續輸入幾次密碼之后,早已被B先生記住了,支開A女士之后使用A女士的銀行卡進行操作。這一通引開注意,誘導操作是不是和剛剛的案例具有異曲同工之妙呢。
那么在程序里,基于OAuth2.0中是如何解決的呢?
回到上面授權碼模式給出的鏈接,你會發現參數中多了一個state參數,通過攜帶的state參數,嗶哩嗶哩就可以通過state進行校驗賬戶的信息是否被篡改。此時的state就相當于當前賬號的sessionid,或cookie的簽名串。
好了,以上我們探討了OAuth的設計原理以及作用。并且拋出了JWT(令牌)的作用和概念,下篇中會詳細講解JWT是如何帶著將令穿梭在端與端之間。
參考資料:
https://www.rfc-editor.org/rfc/rfc6749.html
https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
👇?更多有趣內容,請多關注!👇