refresh token とは
アクセストークンは、APIを呼ぶための短命なトークンです。
Authorization: Bearer access_token
有効期限を短くすると安全性は上がりますが、すぐログインが切れてしまいます。そこで、アクセストークンを更新するために refresh token が使われます。
refresh token -> 新しい access token
refresh token は長く使えることが多く、漏れると危険です。
rotation とは
refresh token rotation は、refresh token を使うたびに新しい refresh token へ入れ替える仕組みです。
refresh_token_A を使う
-> access_token_B と refresh_token_B が返る
-> refresh_token_A は無効になる
古い refresh token を再利用できないようにすることで、漏洩検知や被害抑制がしやすくなります。
なぜ必要か
もし固定の refresh token が盗まれると、攻撃者は長期間アクセストークンを更新し続けられます。
盗まれた refresh token
-> 新しい access token を取得
-> APIを使う
-> 期限切れ後も再取得
rotation があると、同じ refresh token が2回使われた時点で異常を検知できます。
正規ユーザー: refresh_token_A を使う -> Bへ更新
攻撃者: 盗んだ refresh_token_A を使う -> 再利用を検知
ポイント: refresh token rotation は、漏洩を完全に防ぐ仕組みではなく、漏洩時に再利用を検知しやすくする仕組みです。
実装イメージ
OAuthプロバイダーから新しいrefresh tokenが返る場合、古いものを置き換えます。
async function refreshAccessToken(token) {
const res = await fetch("https://provider.example.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: token.refreshToken,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
}),
});
const refreshed = await res.json();
return {
...token,
accessToken: refreshed.access_token,
expiresAt: Date.now() + refreshed.expires_in * 1000,
refreshToken: refreshed.refresh_token ?? token.refreshToken,
};
}
refreshed.refresh_token ?? token.refreshToken としているのは、プロバイダーによって毎回新しいrefresh tokenを返す場合と、返さない場合があるためです。
token family
rotationでは、refresh token の系列を token family として管理することがあります。
同じ古いrefresh tokenが再利用された場合、その系列全体を無効化します。
A -> B -> C -> D
古い B が再利用された
-> C と D も含めて取り消す
これは、攻撃者と正規ユーザーのどちらが現在のトークンを持っているか判断しにくいためです。
ブラウザでの保存場所
refresh token は長期的に強い権限を持つため、保存場所に注意が必要です。
| 保存場所 | 評価 |
|---|---|
| localStorage | 避けたい |
| sessionStorage | 避けたい |
| メモリ | リロードで消えるため扱いが難しい |
| HttpOnly Cookie | 構成次第で候補 |
| サーバ側ストア | 高セキュリティ要件で有力 |
BFF構成では、refresh tokenをブラウザに出さず、BFFやサーバ側ストアに置く設計がよく使われます。
まとめ
refresh token rotation は、refresh token を使うたびに新しいものへ入れ替える仕組みです。
- refresh token は長命で漏れると危険
- rotation により古いtokenを無効化する
- 再利用を検知してtoken familyを取り消せる
- ブラウザのlocalStorageには置かない方がよい
- BFFやサーバ側ストアで扱うと露出を減らせる
参考リソース
- 公式ドキュメント - refresh token rotation とは何か を確認するための一次情報