JWT 的實作安全策略與 Refresh Token 安全做法入門教學筆記 | 學習筆記
一、JWT 基本安全策略
1. 使用強密鑰(Secret)或非對稱加密
- 對稱加密:使用
HS256時,secret應具備高強度(建議 256-bit 以上)。 - 非對稱加密:使用
RS256時,私鑰與公鑰分離,便於服務間驗證。
2. 設定有效期限(exp)
Access Token建議設為 短效(10~30 分鐘)。- 避免永久有效的 Token,防止被盜用後長期濫用。
3. 限制 Token 權限與資訊
- Token 中只放必要資訊(例如:
userId、role)。 - 不要放敏感資料(如密碼、信用卡、住址等)。
4. 透過 HTTPS 傳遞 JWT
- 絕對 禁止在 HTTP 傳輸 JWT,避免中間人攻擊(MITM)。
- 所有 API 傳遞 token(包括 Refresh Token)都必須走 HTTPS。
5. 防範 XSS
- 若將
accessToken存放於localStorage,需格外注意防止前端被注入腳本。 - 建議使用
HttpOnlycookie 儲存 Refresh Token(無法被 JavaScript 存取)。
二、為什麼要使用 Refresh Token?
Access Token 有效期短,提高安全性,但使用者不希望頻繁重新登入,這時就用 Refresh Token 來「悄悄地」幫用戶更新 Access Token。
三、JWT + Refresh Token 實作架構
+-------------+ +----------------+
| 前端客戶端 | | 後端伺服器 |
+-------------+ +----------------+
| |
|--------> /login (帳密登入) --------->|
| |
|<----- accessToken + refreshToken -----|
| |
| 使用 accessToken 存取受保護資源 |
|--------> /api/profile ------------->|
| |
accessToken 過期 ↓ |
|--------> /refresh (更新 token) ------>|
|<------- 新的 accessToken -------------|
四、最佳實作策略詳解
1. accessToken 存放位置
- 可存在記憶體(例如 Redux、React Context)或
localStorage - 優點:操作方便
- 缺點:暴露於 XSS 攻擊風險
2. refreshToken 存放位置
- 建議儲存在 HttpOnly、Secure 的 Cookie 中:
Set-Cookie: refreshToken=xxxxx; HttpOnly; Secure; SameSite=Strict; Path=/refresh
- 前端 JavaScript 無法讀取,降低風險
- 伺服器可以從 cookie 中自動取出 refreshToken
3. 建立 /refresh API(專責刷新 token)
// POST /refresh
import jwt from 'jsonwebtoken';
router.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.status(401).json({ error: '無 refresh token' });
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
const newAccessToken = jwt.sign(
{ userId: decoded.userId, username: decoded.username },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken: newAccessToken });
} catch (err) {
return res.status(403).json({ error: 'Refresh token 無效或過期' });
}
});
4. 可選:Refresh Token 儲存機制(實現強制登出與黑名單)
-
資料庫方案(建議):
- 每次登入時產生唯一的
refreshToken並儲存於 DB - 使用時驗證是否仍存在
- 登出或異常行為時移除對應的 refreshToken
- 每次登入時產生唯一的
-
Redis 快取黑名單方案(進階):
- 遇到帳號盜用或封鎖時,將 token 加入黑名單清單,後續驗證可即時攔截
5. 登出處理(安全地移除 token)
router.post('/logout', (req, res) => {
res.clearCookie('refreshToken', { path: '/refresh' });
res.json({ message: '已成功登出' });
});
五、常見攻擊防護建議
| 威脅類型 | 防護方法 |
|---|---|
| XSS | 不使用 localStorage 儲存 refresh token,輸出內容時做 XSS 過濾 |
| CSRF | 使用 SameSite=Strict 的 cookie,或搭配 CSRF Token |
| Token 竄改 | 使用強加密簽章(如 HS256/RS256)並驗證 |
| 重放攻擊 | 設定合理 exp 過期時間並結合裝置驗證(如 IP / UA) |
| Token 外洩 | refreshToken 實作多設備登入管理與黑名單機制 |
六、小結
| 元素 | 實作策略 | 優點 |
|---|---|---|
| Access Token | 存放在記憶體或 localStorage | 快速存取,搭配短效時安全性高 |
| Refresh Token | 存放於 HttpOnly Cookie | 避免 JS 存取,提高防護能力 |
| /refresh API | 驗證 refreshToken 並產生新 accessToken | 提升使用者體驗與系統可用性 |
| 黑名單策略 | 儲存 refreshToken 資訊於資料庫 | 支援強制登出與封鎖帳號 |
七、額外建議
- 單一使用者只能同時登入 N 台裝置,可依照 refreshToken 綁定設備資訊。
- 將 JWT 實作與中介層(middleware)封裝好,避免開發重複邏輯。
- 若你使用的是框架如 Next.js、NestJS、Spring Boot,也有內建 JWT 與 Refresh 模組可以善用。