~/VibeHandbook
免費 PDF

18 · 04

認證 vs 授權:那個誰都能調的介面

這兩個詞聽起來很像,而二者的區別正是真正的資料洩露藏身之處。

  • 認證(Authentication) 回答的是你是誰?——登入、證明身份。
  • 授權(Authorization) 回答的是你被允許做什麼?——這個已登入的使用者是否可以對這份資料執行這個操作。

AI 在認證上還算不錯;庫幫它處理了大部分。授權才是它經常翻車的地方,因為授權是你應用規則所特有的,而 AI 並不知道這些規則。教科書級的災難:

// VULNERABLE: 檢查了你是否登入,卻沒檢查你是誰
app.get("/admin/export-all-users", requireLogin, (req, res) => {
  res.json(db.getAllUsers()); // 任何已登入使用者都能打這個介面
});

那個介面是“受保護的”——你必須登入。但任何已登入的使用者,包括一個三十秒前才註冊的,都能呼叫它、下載到每一個使用者的資料。認證有了,授權沒有。修復辦法是在操作本身上檢查許可權:

// SAFE: 確認這個使用者確實是管理員
app.get("/admin/export-all-users", requireLogin, (req, res) => {
  if (!req.user.isAdmin) return res.status(403).send("Forbidden");
  res.json(db.getAllUsers());
});

把一個請求必須通過的兩道門想像出來。認證問"你登入了嗎?";授權問"是被允許做這件事嗎?"。當一個應用建了第一道門、卻忘了第二道門時,入侵就發生了:

  請求 ──▶    ┌──────────────────┐  ──▶ ┌──────────────────┐  ──▶  執行  
              │ 認證(authn)      │      │ 授權(authz)       │       動作
              │ "已登入?"        │      │ "該使用者能否       │
              │                  │      │  做此操作?"       │
              └──────────────────┘      └──────────────────┘
                  ✓ 多數應用               ✗ 常常缺失     
                    都做了                  → 即是漏洞   

  示例 — /admin/export-all-users   
     已登入使用者   ──▶ [auth ✓] ──▶ [ 無authz檢查    ] ──▶ 全部使用者被匯出     
     已登入使用者   ──▶ [auth ✓] ──▶ [ isAdmin? → 403  ] ──▶ 被攔截  ✓    

同樣的陷阱以縮小版的形式到處出現:一個介面返回訂單 #1234,卻不檢查這個訂單是不是屬於發起請求的那個使用者。任何人都能改一改 裡的數字,讀到別人的訂單。規則一點也不光鮮,但絕對:在每一個接觸資料的介面上檢查授權,並且不要信任來自客戶端的 ID。 不要因為一個 URL 很隱蔽就假定它安全——“沒人知道這玩意兒存在”不是一種安全控制手段。

想離線閱讀?

免費下載整本書的 PDF 或 EPUB。