JWTの実際とは
JWT(JSON Web Token)とは、クレームを2者間で送信するためのコンパクトでURLセーフな方法です。魔法のようなセキュリティ解決策ではありません。トークン形式です:ヘッダー、ペイロード、署名の3つのbase64urlエンコードされたセグメントがドットで区切られています。
header: {"alg":"HS256","typ":"JWT"}
payload: {"sub":"user123","iat":1700000000}
signature: HMACSHA256(base64url(header)+"."+base64url(payload), secret)
Enter fullscreen mode Exit fullscreen mode
よくある間違い:localStorageにJWTを保存する
やってはいけません。localStorageはJavaScript(XSS)からアクセス可能です。攻撃者がスクリプトを注入すると、トークンが盗まれます。代わりにhttpOnlyクッキーを使用してください。クッキーはリクエストに自動的に送信され、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. ログインエンドポイントがクッキーで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. 各リクエストでトークンを検証するミドルウェア
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. 長時間セッションのためのリフレッシュトークン
アクセストークンは短命(15分)です。リフレッシュトークン(長命で、別のhttpOnlyクッキーに保存)を使用して、再度認証情報を求めずに新しいアクセストークンを取得します。
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:クッキーを使用する場合、CSRF対策が必要です。SameSite=StrictまたはCSRFトークンを使用してください。
- トークンの失効:JWTはステートレスです。ブラックリストを維持しない限り、個別のトークンを失効できません(ステートレス性を損ないます)。ログアウトの場合は、サーバー側でクッキーをクリアするか、短い有効期限を使用してください。
- ペイロードの改ざん:署名により整合性が保証されます。署名を検証せずにペイロードを信頼してはいけません。
よくある落とし穴
- コードにシークレットを保存する:環境変数を使用してください。
-
弱いシークレットを使用する:長くランダムな文字列を使用してください(例:
openssl rand -hex 64)。 - アルゴリズムの検証をしない:サーバーが「none」アルゴリズムを受け入れる場合、攻撃者がトークンを偽造できます。許可されたアルゴリズムを必ず指定してください。
// Explicitly set algorithms
jwt.verify(token, secret, { algorithms: ['HS256'] });
Enter fullscreen mode Exit fullscreen mode
まとめ
JWTはツールであり、銀の弾丸ではありません。httpOnlyクッキー、短命トークン、リフレッシュトークン、適切なCSRF対策を使用してください。トレードオフを理解しましょう:ステートレス性 vs. 失効。シンプルに保ちましょう。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.