iT邦幫忙

2025 iThome 鐵人賽

DAY 14
0

為什麼需要黑名單?

JWT 的特性是「簽發後就是自給自足」——只要驗證簽章與過期時間,伺服器就信任它。

問題來了:如果我想強制某個使用者立即登出,要怎麼辦?

常見場景:

  • 使用者在公共電腦登入,發現忘了登出 → 希望後端能「馬上讓舊 Token 失效」。
  • 管理員停用了帳號 → 不該繼續持有有效 Token。
  • 檢測到異常行為(疑似被盜用)→ 需要緊急撤銷 Token。

如果沒有黑名單,JWT 會在到期前一直有效,無法「主動撤銷」。

因此我們需要 黑名單(Blacklist) 機制。

黑名單的設計思路

  1. Token 標識(jti)

    在簽發 JWT 時,附加一個唯一的 jti(JWT ID)。

    這樣每張 Token 都能被唯一識別。

  2. 黑名單儲存

    當使用者登出、或管理員撤銷權限時,把該 jti 加入黑名單。

    • 可以用 Redis(適合設定 TTL)
    • 或資料庫(如 MySQL, PostgreSQL),但查詢效能要注意。
  3. 檢查機制

    • 每次請求進來時,先驗證 Token 簽章 → 再檢查 jti 是否在黑名單。
    • 如果有,就拒絕請求,回傳 401。
  4. 自動過期

    黑名單項目不需要永久保存,只要設定 TTL = Token 的剩餘壽命,就能自動清除。

強制登出的做法

1. 使用者主動登出

  • 前端呼叫 /logout
  • 後端解析 Token,取出 jti,放進黑名單
  • 下次再用這張 Token 打 API,就會被擋掉

案例

小明在公司電腦登入系統,下班前忘了登出。

他回家後在手機點選「強制登出全部裝置」。

後端就把所有屬於小明的 Refresh Token jti 加進黑名單。

→ 結果:公司電腦的 Token 馬上失效。

2. 管理員強制停用

  • 管理員停用帳號
  • 系統自動把該使用者的所有 Token jti 加入黑名單
  • 使用者手上的 Token 雖然還沒過期,但再打 API 也會被拒絕

案例

客服人員離職,公司 IT 把他的帳號停用。

雖然這個人還握有一張有效的 Token,但因為黑名單檢查,API 都會回 401 Unauthorized

3. Refresh Token 黑名單

除了 Access Token,也要特別處理 Refresh Token。

  • 如果 Refresh 被盜,攻擊者可以一直換新 Access → 風險更大。
  • 所以登出時建議「同時撤銷 Refresh Token」,讓攻擊者完全失效。

案例

小華手機被盜,攻擊者偷到他的 Refresh Token。

後端偵測到可疑登入後,把這個 Refresh Token 加入黑名單,並封鎖其對應的所有 Access Token。

攻擊者就算嘗試 /refresh 也會被拒絕。

延伸設計:黑名單 vs 白名單

  • 黑名單模式

    預設所有 Token 都有效,只有列入黑名單的才無效。

    → 優點:簡單;缺點:需要存黑名單。

  • 白名單模式(Session Store)

    每次登入簽發 Token 時,記錄在伺服器的「有效清單」中。

    API 驗證時除了驗簽,也要查「這張 Token 是否仍在清單內」。

    → 優點:能更精細控管(例如角色改變即失效)。

    → 缺點:失去「完全無狀態」的好處。

實務上:

  • Access Token 常用黑名單(過期快,通常幾分鐘)
  • Refresh Token 常用白名單(因為壽命長,需要嚴格控管)

小結

打了這麼多,我覺得知識量很豐富,但因此變得很冗長,有點枯燥。但這是學習資安很重要的一環,在今天,我們討論了 如何讓 JWT 支援強制登出,方法如下

  • 在 JWT 加上 jti
  • 黑名單存 Redis/DB,TTL 等於 Token 壽命
  • 登出時把 Token 加入黑名單,後續請求自動失效
  • 管理員也能一鍵撤銷使用者所有 Token
  • Refresh Token 必須同樣管控,否則容易被濫用

這就是今天的內容了,感謝大家的收看,我們明天見!


上一篇
Day 13 JWT 過期與 Refresh Token 機制
下一篇
Day 15 JWT 安全性考量
系列文
「站住 口令 誰」關於資安權限與授權的觀念教學,以Spring boot Security框架實作16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言