JWT 究竟是什麼
JWT(JSON Web Token)是一種精簡且 URL 安全的傳遞聲明(claims)的方式。它不是神奇的安全解決方案,而是一種 token 格式:由三個以點(.)分隔的 base64url 編碼片段組成(header.payload.signature)。
header: {"alg":"HS256","typ":"JWT"}
payload: {"sub":"user123","iat":1700000000}
signature: HMACSHA256(base64url(header)+"."+base64url(payload), secret)
Enter fullscreen mode Exit fullscreen mode
常見錯誤:把 JWT 存放在 localStorage
不要這麼做。localStorage 可透過 JavaScript 存取(XSS)。如果攻擊者注入腳本,就能竊取你的 token。請改用 httpOnly cookie。Cookie 會隨請求自動傳送,且無法被 JS 讀取。
// Bad: vulnerable to XSS
localStorage.setItem('token', jwt);
// Better: httpOnly cookie (set by server)
Set-Cookie: token=<jwt>; HttpOnly; Secure; SameSite=Strict
Enter fullscreen mode Exit fullscreen mode
如何實作 JWT 驗證(實務做法)
1. 登入端點以 Cookie 發出 JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = authenticate(username, password);
if (!user) return res.status(401).send('Invalid credentials');
const token = jwt.sign(
{ sub: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.cookie('token', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 15 * 60 * 1000
});
res.json({ message: 'Logged in' });
});
Enter fullscreen mode Exit fullscreen mode
2. 中介軟體在每次請求時驗證 token
function authMiddleware(req, res, next) {
const token = req.cookies.token;
if (!token) return res.status(401).send('No token');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(403).send('Invalid token');
}
}
app.get('/protected', authMiddleware, (req, res) => {
res.json({ user: req.user });
});
Enter fullscreen mode Exit fullscreen mode
3. 使用 refresh token 延長會話
Access token 存活時間很短(15 分鐘)。請使用 refresh token(存活時間較長,存放在另一個 httpOnly cookie 中),在不重新輸入憑證的情況下取得新的 access token。
app.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.status(401).send('No refresh token');
try {
const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
const newAccessToken = jwt.sign(
{ sub: decoded.sub },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.cookie('token', newAccessToken, { httpOnly: true, secure: true, sameSite: 'strict' });
res.json({ message: 'Token refreshed' });
} catch (err) {
return res.status(403).send('Invalid refresh token');
}
});
Enter fullscreen mode Exit fullscreen mode
JWT 無法解決的問題
- CSRF:若使用 cookie,需搭配 CSRF 保護。建議使用 SameSite=Strict 或 CSRF token。
- Token 撤銷:JWT 是無狀態的。除非維護黑名單(這會破壞無狀態特性),否則無法撤銷單一 token。登出時,請在伺服器端清除 cookie 或使用較短的過期時間。
- Payload 竄改:簽章可確保完整性。未驗證簽章前,請勿信任 payload。
常見陷阱
- 將金鑰寫死在程式碼中:請使用環境變數。
-
使用弱金鑰:請使用長且隨機的字串(例如
openssl rand -hex 64)。 - 未驗證演算法:若伺服器接受 'none' 演算法,攻擊者即可偽造 token。請務必指定允許的演算法。
// Explicitly set algorithms
jwt.verify(token, secret, { algorithms: ['HS256'] });
Enter fullscreen mode Exit fullscreen mode
摘要
JWT 是一種工具,而非萬靈丹。請使用 httpOnly cookie、短效 token、refresh token,並妥善實作 CSRF 防護。理解無狀態性與撤銷之間的取捨,保持簡單即可。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.