OAuth2授權原理

最近在做第三方接入的,初步定下使用OAuth2協議,花了些時間對OAuth2的授權方式做了些瞭解。

  我還記得一兩年前,跟一位同事聊起互聯網時,當時我說過一個想法:

  目前不少較爲稀有的資源,很多都是論壇提供下載的,論壇提供的下載往往要求一個論壇帳號,更有甚者,需回帖纔可見,又或者下載需要消耗一定的虛擬貨幣,而這些貨幣可以用論壇活躍度而獲得。假設現在我是一個普通用戶,我要找某個資源。通過搜索引擎或者資料,我發現在某個論壇有這個資源下載,從其他地方獲得這個資源代價比較高或者說根本就找不着。當我準備下載時,很可能就被提示需登錄後纔可下載,隨機被跳轉到註冊頁面。

  爲了這個資源註冊一個帳號?我想,無論誰在99%的情況下都不樂意去註冊一個只是用一次的帳號,偏偏有些論壇就是爲了某些原因要求你必須提供一個帳號。好吧,像我這樣的人,當然是瞎填點信息註冊個帳號了事。至於註冊了帳號需不需要金幣或者多少聲望才能回帖下載之類的,這裏就不嘮叨了。這個過程的關鍵點是:我爲了一個臨時性的需要,註冊了一個永久性無關痛癢的帳號,這個帳號使用一次之後,基本上失去價值了。有無數無聊的用戶花了N多的時間在M多的論壇裏註冊了N*M個無用帳號,這個過程除了對某些統計指標有利以外,對用戶沒有任何價值。

  可不可以做一個平臺,使任意用戶可以在任意論壇註冊一個帳號,隨後這個帳號和密碼自動登記到這個平臺中作爲公共帳號,之後,其他用戶再訪問這個論壇時,就無需再次註冊帳號了,直接在這個平臺上,自動地使用公共帳號去做該做的事。這樣,隨着用戶數的增加,最終可以達到一個比較理想的情況:大部分論壇的臨時性操作,用戶都不用再去註冊了,也不用擔心自己的常用帳號密碼等信息泄漏的問題。儘管對於一些有“經濟系統”的論壇(需要通過活躍度/發帖數/現金等有償獲得虛擬貨幣,存在消費行爲),這個平臺可能不適合,但即使需求只被解決了一半,也是個有價值的產品。

  當時只是大概聊了下,完全沒有動手的打算,至今我還沒發現類似的產品,不知是這個需求不夠大衆還是什麼。那時我也大概看了下OpenID,跟我的設想不一樣,OpenID是將一個用戶在某個平臺上的帳號,公開給其他網站使用,當然公開的只是帳號而不會包含密碼。當時宣傳的口號大概是這樣的:“一次登錄,到處使用”。當時我只在豌豆網註冊了一個OpenID試着玩玩,感覺支持這個OpenID的資源網站太少了,那個帳號作用不大。

  OAuth最近幾年大行其道,很大程度得益於微博的推廣。OAuth和OpenID是比較容易混淆的兩個東西,比較“官方”的觀點認爲:OpenID設計目的是“身份校驗”;OAuth的設計目的是“授權”。我也比較認同這個觀點,但我覺得這種說法本身也挺容易混淆,有位同事說“身份校驗”本身就是對“用戶資源”權限的授予,所以OAuth包含了OpenID的作用。

  在說明我的觀點之前,不妨思考下,目前提供OAuth的網站有那些,他們的提供的服務是什麼,爲什麼他們大多都提供OAuth卻鮮有提及OpenID?(我不是暗指騰訊啦)。

 

OAuth與OpenID

  先看看OpenID,前面多少也講過了,下面以豌豆網爲例:
    服務提供方:豌豆網
    提供的服務:用戶身份識別,同一個用戶有同一個OpenID描述,帳號密碼驗證功能由豌豆網提供
    服務消費方:第三方
    消費的目的:讓豌豆網的用戶來操作自己網站所擁有的資源
  再來對比OAuth,用新浪微博爲例吧:
    服務提供方:新浪微博
    提供的服務:讀取/發送/查詢微博,好友關係處理等,帳號密碼資源都由新浪微博提供
    服務消費方:第三方
    消費的目的:爲終端用戶操作該用戶在新浪微博的資源提供可能
  一對比,區別就很明顯了:OAuth和OpenID的區別主要是服務提供方是否提供有價值的資源。
作爲一個擁有資源的服務提供方,當然希望自己管理自己的用戶信息。假如新浪微博支持其他網站的OpenID登錄,由於有不少的OpenID服務提供方,那麼它需要如何管理自己的用戶呢?例如,用戶A通過網站X的OpenID登錄新浪微博,跟用戶A通過網站Y的OpenID登錄新浪微博,最終的效果是一個帳號還是兩個帳號呢?如果用戶A在新浪微博本身有一個帳號的話,情況又更復雜了。要麼所有帳號都按新帳號處理,要麼提供多個帳號關聯功能。前一種方案簡單易行,但產生了大量非活躍帳號,用戶體驗也不見得好。後一種方案,想一想都覺得,維護是個災難。於是,大多數的資源提供方都傾向與自己管理自己的用戶信息,對於第三方的接入,開放一些授權給他們參與一些用戶資源的訪問就是了,於是便提供OAuth服務而不是提供OpenID接入,一些網站如騰訊還在 OAuth上提供了OpenID。寫着寫着,我自己都覺得OpenID的“接入”和"服務"很拗口。好吧,OpenID的接入是說使用其他網站所驗證的帳號信息,OpenID的服務是指對外提供OpenID的身份校驗服務。
  把上面的情況循環循環再循環,最終,一方面,擁有有價值資源的網站,都做OAuth去了,他們在等待開發者和其他第三方網站的接入,壯大他們的平臺;另一方面,提供OpenID服務的小網站,幾乎沒有大網站的接入支持,對用戶的吸引力越來越小,典型的惡性循環。然後,大部分網站的OAuth服務雖然基本是按照官網規範做接口,但不少細節都做了個性化。例如部分網站的expires_in單位是秒,部分是用分做單位的。部分網站支持state作爲狀態傳遞,部分又不支持。最終這些非標準的東西,會惹惱苦逼的開發者(爲什麼我會很自然的想起IE?),相信很多開發者都會根據市場份額去選擇幾個流行的OAuth提供方進行兼容,其他的,見喬布斯去吧。而用戶則會根據應用的數量去選擇平臺,又一個惡性循環。如果你的資源不夠吸引開發者,就不會有人願意爲你的自定義標準買單。莫非這就是傳說中的,合久必分分久必合?嗯,扯遠了。
  我並沒有貶OpenID褒OAuth的意思,只是覺得在目前市場下,不太可能有大網站願意放棄提供OAuth服務而使用OpenID接入外部帳號。其實我對OpenID瞭解不多,寫着寫着,沒想到竟然寫了一大坨,我真懷疑自己是不是話癆…… 有點晚,明晚繼續,if有空的話。


OAuth授權流程

  OAuth2是從OAuth發展而來的,雖然不向下兼容,但瞭解OAuth能更好的理解OAuth2的一些改變。
OAuth裏存在三個主要角色:用戶、服務提供方和服務消費方。不少文檔會把服務消費方說成是客戶端,對於SP來說,這個說法沒什麼問題,但我感覺這個說放容易引起混淆,所以我這裏還是用服務消費方來描述。按流行的口號,服務提供方一般對外宣稱自己是某某某開放平臺,而服務消費方則是各種第三方應用。用戶在平臺上有一些已有資源,如好友關係,照片等。

  幾乎所有的OAuth平臺都有類似的背景:他們原先積累了一大堆的真實用戶,在互聯網開放的趨勢下,主動或被動的需要支持第三方應用的接入。第三方應用爲了使其功能更加豐富完整,希望從平臺能獲取甚至操作當前用戶的資源。用戶很可能不希望第三方得知他原有的帳號和密碼,原因很明顯,安全考慮嘛。服務提供方也不希望第三方直接使用用戶的帳號和密碼登錄平臺操作用戶數據,爲啥?不便於數據統計和維護嘛,希望對 哪個第三方操作哪個用戶數據 和 哪個用戶操作自己的數據 兩種處理流程有所區別。第三方很無辜,經常大喊“我覺不會使用任何途徑存儲用戶的帳號!”。即使真有人相信這些誓言,但也很難確保第三方使用帳號敏感數據時,不被第四方所捕獲,所以,認真你就輸了。

  爲了解決上面的問題,準確的說是讓三種角色互相信任,OAuth由此而生。在沒有第三方的情況下,服務提供方和用戶可以認爲是互相信任的,因爲用戶用域名來確保自己訪問的是一個受信的站點;服務提供方則要求用戶登錄,並且登錄會話可以控制。

  應爲第三方一般是不知名的,用戶很難區分第三方合不合法,所以用戶需要通過服務提供方來證實第三方,例如位於服務提供方的OAuth授權頁面會簡單的介紹該應用的簡單介紹,正是這些介紹使得用戶可以相信,該應用是一個合法登記的第三方。

  爲了讓服務提供方信任第三方應用,第三方應用在必要時需要向服務提供方提供身份憑據。最簡單的辦法就是第三方開發者去服務提供方那去註冊個帳號,然後在需要時用這個帳號來證明自己的身份。這種第三方應用的帳號,下面統稱應用帳號。由於第三方的請求不會有人工的干預,所以應用帳號的帳號密碼一般由服務提供商提供,方便服務提供方管理,安全係數也較高,因爲服務提供方可以制定規則,使密碼更難以僞造或猜測。

  按理說,第三方應用除了到SP處申請一個應用帳號外,也有其他辦法證實自己的身份。

  例如可以使用HTTPS連接,讓“第四方”去證明。OAuth2使用的就是HTTPS連接,但也僅僅是服務端認證,客戶端並不做保證。估計一個方面的原因是,應用的數量很多,一般都是中小規模開發商開發的,客戶端也要認證的話,證書申請門檻較高,一個賬號密碼可以解決的問題有必要去申請證書嗎?另一方面是,很多應用是沒有服務端的,使用雙向HTTPS認證無疑將這些應用拒之門外。

  上面的方法是,用戶通過服務提供方,去識別第三方是否合法。還有種方式是:服務提供方通過用戶,去識別第三方是否合法。但OAuth裏沒有這種方式的體現,但OAuth2裏有類似的方式,那就是提供用戶的帳號密碼換取AccessToken,名字應該叫“資源所有者密碼憑據”。如果第三方應用只是開發者自娛自樂的小應用,這種方式是最簡單的。

  經過上面的註冊和授權流程,用戶和服務提供方都可以確認第三方應用的身份了,那第三方如何確認服務提供方和用戶的身份?

  第三方應用怎麼確認服務提供方的身份呢?很簡單,域名就是服務提供方的唯一標識,只要DNS不被劫持的話。第三方應用根據服務提供方的返回內容確認用戶身份,載體是操作令牌AccessToken,爲了方便後面統稱ATOK,在OAuth裏,ATOK的有效期是從用戶授權成功,到用戶取消授權,對第三方來說,幾乎是永久的。至於用戶授權之後取消授權,再授權的時候,兩次ATOK是否一樣,第三方能否處理好這種情況,OAuth裏沒有提及,看實現者的心情了。

   把上面所說的綜合在一起,可以得到一個OAuth的雛形版本:

  第三方到服務提供方註冊個應用帳號,當需要操作用戶在服務提供方處的數據時,提供應用帳號密碼申請授權,服務提供方將用戶引導到授權頁面,當授權成功時,服務提供方將對應該用戶的ATOK發給應用,隨後應用就使用這個ATOK來操作用戶數據。

  下面新浪微博OAuth的基本流程(其實各平臺的流程都一樣,貼這個是覺得這張圖比較好看):

新浪微博OAuth基本流程

  從圖中可以看到OAuth的流程比原先設想的雛形多了不少東西,這些多出來的有什麼作用呢?

  四個步驟

  OAuth授權分四步。

  第一步,應用向服務提供方申請請求令牌(Request Token),服務提供方驗證通過後將令牌返回。這個步驟由於涉及到應用帳號密碼,在應用的服務端發起,所以這個步驟對用戶透明。

  第二步,應用使用請求令牌讓瀏覽器重定向到服務提供方進行登錄驗證和授權。服務提供方校驗請求令牌,將第三方的資料顯示給用戶,提示用戶選擇同意或拒絕此次授權。如果用戶同意授權,發放已授權令牌並將用戶引導到當前應用的註冊地址。這個步驟從重定向開始到引導回註冊地址之前,應用方並不參與用戶身份校驗和授權過程,確保第三方不可獲得用戶的真實帳號密碼。

  第三步,用已授權令牌向服務提供方換取ATOK。第三方應用需在服務端發起請求,用帳號密碼和上一步的令牌換取ATOK,這個步驟對用戶而言也是透明的。如果前兩步分別是讓服務提供方認證應用和用戶,那這步就是用戶和服務提供方再次認證第三方應用。因爲用戶瀏覽器將第二步的結果重定向到第三步,除非用戶DNS被劫持,否則就能確保重定向到的是合法的地址。曾經我很困惑在用戶授權之後爲何不直接返回ATOK而需要再次換取,估計是出於對ATOK的安全考慮,用戶瀏覽器一端存在太多的可能性讓ATOK泄漏,最安全的辦法還是讓第三方服務端來獲取和保管ATOK。

  第四步,用ATOK作爲令牌訪問受保護資源。很多時候,權限是有多種類別的。ATOK包含了某個用戶對某個應用的授權憑據,準確的說,ATOK對應用戶授權時所賦予的一系列權限的集合。所以在這一步,除了校驗ATOK的合法性之外,服務提供方還需對該ATOK是否擁有足夠的權限執行被保護操作進行判斷。

  單次簽名

  在OAuth裏,OAuth的相關請求都要做單次簽名,目的是防止OAuth的請求被篡改和重放。簽名當然是拿應用帳號的密碼來做簽名,其實就是對HTTP請求中所有OAuth相關的參數都連在一塊,使用密碼計算某種哈希值作爲簽名。OAuth規範裏描述了簽名的規則,那是相當的繁瑣、複雜,足以嚇跑一大堆未經世事的開發者。隨便找一個OAuth開放平臺的API文檔,我相信在OAuth授權流程有接近一半會在描述怎麼產生簽名構造一個合法的HTTP請求。有一對文字圖片描述還不夠,各開放平臺幾乎無一例外地提供各種開發語言下的SDK,爲求儘量降低技術門檻。即使如此,不少開發者依然覺得,OAuth的簽名過程實在是太複雜了,而這些複雜也沒有帶來預期的好處。

  重定向地址

  爲了防止有攻擊者僞造重定向地址騙取用戶授權,服務提供方應對授權時的重定向地址進行驗證。所以註冊時,第三方應提供重定向地址。服務提供方可以直接對重定向地址進行等值判斷,但這樣的話就沒辦法讓第三方在授權過程中傳遞狀態,只能藉助Cookie/Session之類的方式了。服務提供方也可以判斷重定向地址是否同一個域,這樣的話應用方就可以在URI裏傳遞少量狀態。對於一些沒有服務端的第三方Web應用,由於代碼是公開的,將應用的帳號密碼存在頁面裏並不合適。OAuth則建議不使用重定向地址,讓用戶在授權後,把授權碼人工輸入到應用中進行下一步。記得有段時間FaWave也是這麼添加新帳號的。

  安全漏洞

  OAuth曾爆了一個安全漏洞,攻擊者利用此漏洞可騙取用戶信任獲取非法的授權。

  這個網頁有該漏洞的詳細說明,流程如下:

  OAuth授權漏洞

  簡單的說,這個漏洞主要的關鍵是:

    1. 部分服務提供方並未對重定向地址進行合法性判斷,或者部分第三方的重定向地址會根據URI的參數再次重定向從而被攻擊者利用;

    2. RequestToken從未授權到已授權的狀態轉變時沒有變化,從而爲攻擊者暴力訪問回調地址騙取ATOK提供可能;

  對於第一點,攻擊者僞造重定向地址,即可騙得用戶對可靠第三方的授權,獲得ATOK

  對於第二點,假如第一點不成立,那攻擊者可以用第一步的請求令牌構造一個合法的重定向請求,並在用戶授權之後、瀏覽器重定向到合法重定向地址之前,進行同樣操作執行這個重定向操作,此時就看攻擊者和正常授權流程就存在競爭關係。如果第三方先處理攻擊者的請求,攻擊者就獲得了最終的ATOK。

  爲了解決上述安全漏洞,OAuth更新了1.0a版本,主要改變就是第一步增加對重定向地址的簽名,和第二步與第三步之間增加一個隨機校驗碼,使之與未授權的RequestToken有所區分。

   目前大部分的平臺都轉到了OAuth2。OAuth2雖並不兼容OAuth1,但基本原理是一樣的。

 

OAuth2的改變

  OAuth2對比OAuth1,主要改變有下面幾點:

    1. 取消繁瑣的簽名,全部改用HTTPS。

    2. ATOK從原來的永久令牌變爲臨時令牌,增加RefreshToken

    3. 取消獲取RequestToken的步驟

    4. 提供了多種場景的授權流程

  HTTPS

  OAuth原有的簽名算法實在是太繁瑣了,嚇跑了不少開發者。對於服務提供方,也很不好實現,特別是單次簽名的實現,由於服務提供方要確保每次由客戶端生成的隨機碼不被重複利用,必須存儲每次請求發來的隨機碼,無論是對存儲還是校驗都是一個難題。通常的做法是,存儲一段時間的隨機碼,這個時間需比RequestToken的過期時間要長。這樣即使到時還有重放攻擊,RequestToken也已經失效。

   OAuth2取消了簽名,改用HTTPS來加密,確保通信內容不被第三方竊取。這個改變毫無疑問是降低了門檻,授權的流程被簡化了。雖有少量人有異議,但OAuth2最大的異議是臨時的ATOK。

  ATOK與RefreshToken

  由於第三方應用往往不重視ATOK的安全性,開發者爲圖方便經常把ATOK從後端發給前端頁面或者存在cookie中。由於OAuth1中ATOK幾乎是永久性的,即使發現ATOK被盜用,也只能讓用戶取消授權,這可能會造成一些其他的問題。OAuth2將ATOK改爲臨時令牌,當ATOK過期後,需要使用RefreshToken重新獲取新的ATOK,讓開發者鬱悶的是,RefreshToken也不是永久性的,不同的服務提供方有不同的過期時間,相同的是,過期時間都不會太長,頂多也就幾個月。

  這個改變對很多第三方應用是個坑爹的改變,原本他們幾乎都是拿ATOK作爲OpenID來使用的(所以纔有了各種ATOK被盜用的隱患),而到了OAuth2,ATOK已經不能唯一標識一個用戶了,他們要多做很多的東西才能維持用戶的身份,在使用ATOK訪問用戶資源時,步驟也是異常繁瑣。

  雖然臨時ATOK這個改變很合理,但對開發者很不友好,今後會不會繼續改變,可以拭目以待。我個人覺得,這個問題其實是另外一個問題,那就是開發者對用戶帳號信息的安全意識太單薄,後面講OAuth的問題時再詳細討論。

  授權流程

  OAuth1流程比較複雜,儘管規範裏有對多種場景的授權流程進行不同的建議,但很多應用和開放平臺最終都使用了同一種授權流程,結果產生了安全隱患(例如上面重定向地址的問題嗎)。OAuth2描述了四種授權場景,爲這些場景下的授權流程提供指導。我只簡單說些要點和差異,詳細的說明還是看官方文檔和各開放平臺的文檔穩妥些。

  (待續)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章