移花接木:針對OAuth2的攻擊

ThoughtWorks發表於2017-07-20

作為第三方應用,為了提升使用者體驗,往往會提供第三方社交賬號登入或者繫結的功能,這背後使用到的關鍵技術是OAuth認證。想要在自己的應用裡整合OAuth不是難事兒,各大社交網站都提供了詳盡的文件指南。

OAuth的複雜度比較高,有不少安全方面的坑,開發者在使用過程中一不注意可能就會掉進去,比如說不正確的使用OAuth2可能會遭遇到CSRF攻擊。本文將對這個安全風險做一個通俗易懂的解釋。 老司機發車前的特別提醒:閱讀本文需要你對OAuth2有一些瞭解,可以參考文末的連結。

1. OAuth2 授權模式回顧

在開始之前,讓我們先來回顧一下OAuth2中最典型的Authorization Code 授權模式,其大致流程如下:

圖1:OAuth2 Authorization Code Flow

我們把OAuth2的整個認證過程大致分為三個階段。第一階段主要是向使用者取得授權許可,對應圖中的第1、2、3步;第二階段主要是申請訪問令牌(access_token),對應圖中的第4、5步;第三階段就是使用access_token獲取使用者資料。

這一過程中涉及了不少敏感引數和資料,例如client_secret相當於是第三方應用自己的密碼,access_token某種程度上來講就是使用者的session id。由於這些引數以及資料極其特殊,我們當然得確保它們的安全性,HTTPS加密傳輸以及安全儲存是必不可少的防護手段。不過僅僅做到這些是遠遠不夠的,在這個流程裡存在一個弱點,容易被攻擊者利用進行CSRF攻擊。

2. 針對OAuth2的CSRF攻擊

2.1 攻擊流程

讓我們來看一個針對OAuth2的CSRF攻擊的例子。假設有使用者張三,和攻擊者李四,還有一個第三方Web應用Tonr,它整合了第三方社交賬號登入,並且允許使用者將社交賬號和Tonr中的賬號進行繫結。此外還有一個OAuth2服務提供者Sparklr。

圖2:模擬攻擊案例中涉及的角色

Step 1. 攻擊者李四登入Tonr網站,並且選擇繫結自己的Sparklr賬號。

Step 2. Tonr網站將李四重定向到Sparklr,由於他之前已經登入過Sparklr,所以Sparklr直接向他顯示“是否授權Tonr訪問”的頁面。

Step 3. 李四在點選”同意授權“之後,截獲Sparklr伺服器返回的含有Authorization Code引數的HTTP響應。

Step 4. 李四精心構造一個Web頁面,它會觸發Tonr網站向Sparklr發起令牌申請的請求,而這個請求中的Authorization Code引數正是上一步截獲到的code。

Step 5. 李四將這個Web頁面放到網際網路上,等待或者誘騙受害者張三來訪問。

Step 6. 張三之前登入了Tonr網站,只是沒有把自己的賬號和其他社交賬號繫結起來。在張三訪問了李四準備的這個Web頁面後,令牌申請流程在張三的瀏覽器裡被順利觸發,Tonr網站從Sparklr那裡獲取到access_token,但是這個token以及通過它進一步獲取到的使用者資訊卻都是攻擊者李四的。

Step 7. Tonr網站將李四的Sparklr賬號同張三的Tonr賬號關聯繫結起來,從此以後,李四就可以用自己的Sparklr賬號通過OAuth登入到張三在Tonr網站中的賬號,堂而皇之的冒充張三的身份執行各種操作。

等等,這一切發生得太快,還沒看清楚李四怎麼就登入到張三的賬號裡去了。沒關係,讓我們從幾個不同的角度來看看這當中發生了什麼。

2.2 受害者張三(Resource Owner)視角

受害者張三訪問了一個Web頁面,然後,就沒有然後了,他在Tonr網站上的賬號就和攻擊者李四在Sparklr上的賬號繫結到了一起。偽造的請求是經過精心構造的,令牌申請這一過程在張三的瀏覽器裡是非常隱蔽的被觸發的,換句話講就是,他根本不知道這背後發生了什麼。

2.3 Tonr網站(Client)視角

從Tonr網站來看,它收到的所有請求看上去都是正常的。首先它收到了一個HTTP請求,其代表著當前使用者張三在Sparklr網站上已經做了“同意授權”操作。其內容如下:

不過需要注意的是,URL裡的code不是當前受害者張三的Authorization Code,而是攻擊者李四的。

當Tonr收到這樣的請求時,它以為張三已經同意授權(但實際上這個請求是李四偽造的),於是就發起後續的令牌申請請求,用收到的Authorization Code向Sparklr換取access_token,只不過最後拿到的是攻擊者李四的 access_token。

最後,Tonr網站把攻擊者李四的access_token和當前受害者張三在Tonr網站上的賬號進行關聯繫結。

2.4 Sparklr網站(OAuth2服務提供者)視角

Sparklr網站也是一臉茫然的樣子,因為在它看來,自己收到的授權請求,以及後續的令牌申請請求都是正常的,或者說它無法得知接收到的這些請求之間的關聯關係,而且也無法區別出這些請求到底是來自張三本人,還是由李四偽造出來的。因此只要自己收到的引數是正確有效的,那就提供正常的認證服務,僅此而已。

2.5 攻擊者李四視角

李四偽造了一個使用者授權成功的請求,並且將其中的Authorization Code引數替換成了自己提前獲取到的code。這樣,當受害者張三的瀏覽器被欺騙從而發起令牌申請請求時,實際上是在用張三在Tonr網站上的賬號和李四在Sparklr網站上的賬號做繫結。

攻擊完成後,李四在Tonr網站上可以通過自己在Sparklr網站的賬號進行登入,而且登入進入的是張三在Tonr網站上的賬號。而張三通過自己在Tonr網站上的賬號登入進去之後,看到的是李四在Sparklr網站上的資料。

2.6 上帝視角

從整體上來看,這次攻擊的時序圖應該是下面這個樣子的:

圖3:攻擊時序圖示

3. 漏洞的本質

這個問題的關鍵點在於,OAuth2的認證流程是分為好幾步來完成的,在圖1中的第4步,第三方應用在收到一個GET請求時,除了能知道當前使用者的cookie,以及URL中的Authorization Code之外,難以分辨出這個請求到底是使用者本人的意願,還是攻擊者利用使用者的身份偽造出來的請求。 於是乎,攻擊者就能使用移花接木的手段,提前準備一個含有自己的Authorization Code的請求,並讓受害者的瀏覽器來接著完成後續的令牌申請流程。

4. 前提條件

儘管這個攻擊既巧妙又隱蔽,但是要成功進行這樣的CSRF攻擊也是需要滿足一定前提條件的。

首先,在攻擊過程中,受害者張三在Tonr網站上的使用者會話(User Session)必須是有效的,也就是說,張三在受到攻擊前已經登入了Tonr網站。

其次,整個攻擊必須在短時間內完成,因為OAuth2提供者頒發的Authorization Code有效期很短,OAuth2官方推薦的時間是不大於10分鐘,而一旦Authorization Code過期那麼後續的攻擊也就不能進行下去了。

最後,一個Authorization Code只能被使用一次,如果OAuth2提供者收到重複的Authorization Code,它會拒絕當前的令牌申請請求。不止如此,根據OAuth2官方推薦,它還可以把和這個已經使用過的Authorization Code相關聯的access_token全部撤銷掉,進一步降低安全風險。

5. 防禦辦法

要防止這樣的攻擊其實很容易,作為第三方應用的開發者,只需在OAuth認證過程中加入state引數,並驗證它的引數值即可。具體細節如下:

  • 在將使用者重定向到OAuth2的Authorization Endpoint去的時候,為使用者生成一個隨機的字串,並作為state引數加入到URL中。
  • 在收到OAuth2服務提供者返回的Authorization Code請求的時候,驗證接收到的state引數值。如果是正確合法的請求,那麼此時接受到的引數值應該和上一步提到的為該使用者生成的state引數值完全一致,否則就是異常請求。
  • state引數值需要具備下面幾個特性:
    • 不可預測性:足夠的隨機,使得攻擊者難以猜到正確的引數值
    • 關聯性:state引數值和當前使用者會話(user session)是相互關聯的
    • 唯一性:每個使用者,甚至每次請求生成的state引數值都是唯一的
    • 時效性:state引數一旦被使用則立即失效

6. 總結

要避免遭受本文提到的CSRF攻擊問題,需要第三方應用正確的使用state引數,然而縱觀各大OAuth服務提供者,在其開發文件裡都沒有明確把state引數和CSRF攻擊聯絡起來,僅僅只是像下面這樣一句話帶過:

“引數:state

是否必須:否

說明:重定向後會帶上state引數,開發者可以填寫a-zA-Z0-9的引數值,最多128位元組”

讓事情變得更糟糕的是,state是可選引數,因此更容易被開發者忽略,造成安全風險。此外,本文中的攻擊非常巧妙,可以悄無聲息的攻陷受害者的賬號,難以被察覺到。

作為第三方應用的開發者,我們除了參考OAuth2服務提供者的開發文件之外,還應當加深自己對OAuth2的理解,儘可能的避開這些安全的坑。 而作為OAuth2服務提供者,也應當承擔起提醒開發者注意防範安全風險的責任。

參考資料

相關文章