安全性是所有 Web 應用的關鍵要素,對 API 而言更是如此。新的安全漏洞不斷被發現,安全性漏洞可能導致關鍵資料與營收的重大損失。
除了一般 Web 應用的安全實踐(輸入驗證、SSL、內容類型驗證、稽核日誌、防禦 CSRF 與 XSS)之外,暴露給外部開發者的 Web API 還需要額外的安全技術。本章將深入探討這些最佳實踐。
認證與授權#
安全性的兩大基石:
| 概念 | 定義 | 範例 |
|---|---|---|
| 認證(Authentication) | 驗證「你是誰」 | 通常透過使用者名稱和密碼登入來完成 |
| 授權(Authorization) | 驗證「你能做什麼」 | 允許瀏覽頁面但不允許編輯,除非你是管理員 |
Basic Authentication 的問題#
Basic Authentication 是最簡單的存取控制技術——客戶端發送 Authorization: Basic <base64 編碼的帳號:密碼> 標頭。但它的安全性最低:
- 應用程式必須以明文或可解密的方式儲存憑證,一旦洩漏影響嚴重
- 使用者無法撤銷單一應用程式的存取權,只能改密碼一次撤銷全部
- 應用程式獲得使用者帳號的完整存取權限,無法限制範圍
注意: 基於上述原因,Twitter 在 2010 年停止了核心 API 對 Basic Authentication 的支援。
OAuth#
OAuth 是一個開放標準,於 2007 年推出,允許使用者在不分享密碼的情況下授予應用程式存取權限。目前的 OAuth 2.0 已是業界標準,被 Amazon、Google、Facebook、GitHub、Stripe 和 Slack 等公司採用。
OAuth 的三大優勢:
- 無需分享密碼:使用者被重新導向至 API 提供者進行授權
- 選擇性授權:使用者可精確控制授予哪些權限
- 可撤銷存取:使用者隨時可撤銷特定應用程式的存取權,無需更改密碼

Figure 3.1: The OAuth flow between TripAdvisor and Facebook
Token 產生流程#
OAuth 應用程式使用 access token 代表使用者呼叫 API。Token 的產生是一個多步驟流程:
前置準備: 應用程式向 API 提供者註冊,提供 redirect URL,取得 client ID(可公開)和 client secret(需保密)。
授權流程:
- 應用程式引導使用者至 API 提供者授權 — 傳送 client ID、請求的權限範圍(scope)、可選的 state 參數和 redirect URL
- API 提供者向使用者請求授權 — 清楚顯示應用程式請求的權限。使用者同意後,帶著 authorization code 重導回應用程式
- 應用程式以 authorization code 換取 access token — 提供 client ID、client secret、authorization code 和 redirect URL

Figure 3.2: Authorization screen presented by Slack to users

Figure 3.3: OAuth 2.0 access token grant flow
Scopes(權限範圍)#
OAuth scopes 用於限制應用程式對使用者資料的存取。API 提供者在授權時會向使用者顯示所有請求的 scopes。
Twitter API 的三層權限範例:
| 權限層級 | 說明 |
|---|---|
| Read only | 唯讀 |
| Read and write | 讀寫 |
| Read, write, and access direct messages | 讀寫 + 私訊存取 |
重點: 在 API 設計初期就思考 scopes 策略。事後引入新的 scopes 會非常複雜。粒度太粗讓應用程式權限過大,粒度太細則讓使用者和開發者困惑。
定義 scopes 的進階考量:
| 設計策略 | 說明 |
|---|---|
| 最小 scope | 僅提供基本使用者資訊(姓名、頭像),用於登入識別 |
| 敏感資訊隔離 | 以獨立 scope 保護敏感資料,授權時顯示明確警告 |
| 按資源類型區分 | 例如 Slack 針對訊息、頻道、使用者等不同資源分別設定讀寫 scopes |
案例:Slack 轉向細粒度 OAuth Scopes
Slack 最初的 OAuth scopes 範圍很廣——授權 read scope 意味著應用程式可以讀取所有訊息、頻道、表情反應、檔案等資源。2015 年底,Slack 引入了 27 個細粒度 scopes,為每種資源類型分別提供讀寫權限。
好處是應用程式只獲得執行其功能所需的權限,使用者也更願意授權給存取範圍有限的應用程式,安裝轉換率因此提升。
Token 與 Scope 驗證#
API 伺服器收到請求時需要驗證兩件事:
- Access token 是否有效 — 與資料庫中已授權的 token 比對
- Token 是否具備所需 scope — 確認權限足夠
curl -H "Authorization: token OAUTH-TOKEN" \
https://api.github.com/users/saurabhsahni -I
HTTP/1.1 200 OK
X-OAuth-Scopes: repo, user
X-Accepted-OAuth-Scopes: user技巧: 當 token 缺少必要的 scope 時,回傳詳細錯誤訊息,包含已提供的 scope 和所需的 scope,能大幅減少開發者的除錯時間。Slack API 就採用了這種做法。
Token 過期與 Refresh Token#
許多 API 選擇發行數小時或數天後過期的 access token。若 token 被洩漏,影響範圍有限。
Refresh token 是一種特殊 token,用於在 access token 過期時取得新的 token。應用程式提供 client ID、client secret 和 refresh token 即可更新。Google、Salesforce、Stripe 等都支援此機制。
補充: 短期 access token 更安全的原因——即使 access token 被洩漏,僅在過期前有效;refresh token 被洩漏但沒有 client secret 也無法使用;兩者同時被洩漏時,因 refresh token 通常為一次性使用,可偵測到異常。
案例:Slack 的長期 Token 教訓
Slack 的 token 原本是長期有效的。許多開發者在 OAuth 支援推出前就「破解」方式取得 token 並直接嵌入程式碼,有些人甚至不小心將 token 發布到 GitHub 上。
Slack 因此建立了一個爬蟲程式,自動搜尋 GitHub 開源碼庫中的 Slack token,發現後自動撤銷並通知開發者。2018 年 5 月,Slack 宣布將引入短期 access token。
列出與撤銷授權#
使用者需要知道哪些應用程式可以存取他們的資料,並能隨時撤銷存取。API 提供者通常提供授權管理頁面和程式化撤銷 API。

Figure 3.4: Twitter's page listing authorized applications with revoke access
OAuth 最佳實踐#
建構自己的 OAuth 授權伺服器時,應考慮以下實踐:
| 實踐 | 說明 |
|---|---|
| 支援 state 參數 | 用於防禦 CSRF 攻擊 |
| 短期 authorization code | 數分鐘內過期且僅限使用一次 |
| 一次性 refresh token | 儲存高度敏感資料的應用程式可考慮,Fitbit API 允許兩分鐘內重複使用以應對網路失敗 |
| 可重設 client secret | 讓開發者在洩漏時能立即輪換 |
| 敏感資訊專用 scope | 避免使用者不必要地授權存取敏感資料 |
| 強制 HTTPS | 防止中間人攻擊 |
| 驗證 redirect URL | 必須與註冊的 URL 匹配,否則顯示錯誤 |
| 禁止 iframe 渲染授權畫面 | 使用 X-Frame-Options 防止點擊劫持 |
| 通知使用者 | 新授權時透過電子郵件等管道通知 |
| 禁止誤導性應用程式名稱 | 防止釣魚攻擊 |

Figure 3.5: A malicious non-Google app named 'Google Docs' phished a million accounts
警告: 2017 年,攻擊者建立了一個名為「Google Docs」的 OAuth 應用程式,成功釣魚了一百萬個 Google 帳號。務必禁止應用程式使用可能誤導使用者的名稱。
案例:Facebook 的教訓——信任的平衡
2016 至 2018 年間,Facebook 的政策因允許開發者大量挖掘客戶資料而受到嚴厲批評。關鍵啟示:
- 確保客戶清楚知道他們同意了什麼——OAuth 流程中明確顯示權限
- 主動監控第三方應用是否違反服務條款
- 信任關係存在於你和客戶之間,而非客戶與第三方應用之間——你對平台的信譽負有更大責任
WebHook 安全性#
WebHook URL 通常在網際網路上公開可存取,因此開發者必須能驗證 POST 請求確實來自聲稱的發送者。目前沒有像 OAuth 這樣的標準,但有幾種常見模式。
Verification Token#
API 提供者為每個應用程式發行一個唯一的 verification token,隨每次 WebHook 請求一起發送。應用程式比對收到的 token 與記錄值來驗證來源。

Figure 3.6: Credentials of a Slack app, including verification token
注意: Verification token 以明文傳送,安全性有限。若 token 洩漏或被破解,攻擊者可偽造 WebHook 請求。
Request Signing(請求簽章)#
更安全的方式是使用 HMAC 簽章——以共享密鑰和請求本體計算雜湊值,將簽章放在請求標頭中。Stripe 和 GitHub 都使用此機制。
Stripe-Signature: t=1492774577,
v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0c,
v0=6ffbb59b2300aae63f272406069a9788598b792a944a07aba816ed技巧: 在 payload 中包含時間戳(如 Stripe 的
t=1492774577),可防禦重播攻擊(replay attack)——若時間戳過舊,應用程式即拒絕該請求。
Mutual TLS#
伺服器和客戶端雙方互相驗證憑證。與 request signing 在應用程式層級實作不同,Mutual TLS 在更低層級實作,開發者無需額外編寫程式即可強制執行高安全性。特別適用於企業級 B2B 應用。
Thin Payload + API 查詢#
一個更安全的選項是在 WebHook payload 中只發送最少量的資訊,通知應用程式「有東西改變了」。應用程式需要透過後續的 API 請求(需認證)來取得完整事件。
Gmail API 就使用這種方式:WebHook 只包含 email 地址和變更 ID,應用程式必須呼叫 history.list API 取得完整詳情。
重點: 此方法的關鍵好處——即使應用程式未驗證 WebHook,也只有透過正規的認證 API 請求才能取得完整事件資料。
WebHook 安全最佳實踐#
| 實踐 | 說明 |
|---|---|
| 避免在 WebHook 中傳送敏感資訊 | 密碼或密鑰一律透過認證 API 請求傳送 |
| 簽章中包含時間戳 | 讓應用程式能偵測重播攻擊 |
| 支援共享密鑰的重新產生 | 洩漏時可輪換 |
| 提供 SDK 和範例程式碼 | 驗證 WebHook 請求的真實性並拒絕無效請求 |
本章小結#
安全性很難,API 安全性更難。一旦有應用程式使用了某種安全機制,要更改就非常困難——一個漏洞可能需要大量開發者修補其應用程式。
警告: 不要試圖發明自己的安全機制。除非有安全專家設計和審查,否則很難確保沒有漏洞。依賴經過多年駭客和專家測試的開放標準,遇到重大安全漏洞的機率會低得多。