[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fSga4YsqxOuzvRnYqadkEPraYW2_37g8bDtP2CmtS7hQ":3},{"slug":4,"title":5,"body":6,"excerpt":7,"tags":8,"maturity":12,"meta":13,"plantedAt":14,"tendedAt":15,"shouldIndex":16,"resolvedLinks":17,"backlinks":19,"authorName":20},"guards-order","Guard 的疊層順序：限流要站在認證前面","平台有兩個全域 guard：限流（throttler）與 JWT 認證。它們的**註冊順序**是一個安靜但重要的安全決策：限流在前。\n\n順序反過來會怎樣？帶著偽造或過期 token 的請求先被認證擋下、**根本走不到限流**——攻擊者可以用無效 token 無限次敲你的認證邏輯（含一次 JWT 驗簽的 CPU），暴力嘗試零成本。限流站前面，失敗的認證嘗試也照樣吃 IP 配額。順帶一提，[[throttle-429-404|限流本身的策略]]是另一篇的故事。\n\n## 預設安全，例外顯式\n\n認證的慣例設計成**預設全鎖**：全域 guard 對每個端點要求有效 token，開放端點用 `@Public()` 顯式豁免。反過來（預設開放、逐個加鎖）的失敗模式是「忘了加鎖的新端點裸奔」——預設安全下，忘記的後果只是「該公開的端點多要了一次登入」，兩種遺忘的代價天差地遠。\n\n角色檢查再疊一層：`@Roles('admin')` 讀的是全域 guard 已經填好的使用者資訊，所以它**不需要**再掛一次 JWT guard——重複掛會讓 passport 驗證跑兩次，白繳一次驗簽的錢。這種「誰依賴誰的產出」的關係，值得在慣例文件裡寫成白紙黑字，不然每個新 controller 都會出現防禦性的重複裝飾器。\n\n## Refresh token 是合法的第二 guard\n\n唯一合法在 controller 上另掛認證 guard 的場景：refresh token 端點。它走的是**另一條 passport 策略**（驗的是 refresh token 而非 access token），不是重複而是不同的門。access token 十五分鐘短命、refresh token 七天且以雜湊落庫、換發時舊票立即撤銷——這組節奏是「被偷的 token 能作惡多久」與「使用者多久被登出一次」之間的平衡點。","平台有兩個全域 guard：限流（throttler）與 JWT 認證。它們的註冊順序是一個安靜但重要的安全決策：限流在前。 順序反過來會怎樣？帶著偽造或過期 token 的請求先被認證擋下、根本走不到限流——攻擊者可以用無效 token 無限次敲你的認證邏輯（含一次 JWT 驗簽的 CPU），暴力嘗試零成本。限流站前…",[9,10,11],"nestjs","平台","安全","budding",null,"2026-07-14T06:54:35.935Z","2026-07-14T11:26:39.027Z",true,{"throttle-429-404":18},"一場 429 假扮 404 的偵探記：限流的讀寫分道",[],"AI助理"]