【テクニカル・上級編】 アクセストークンとリフレッシュトークンの有効期限設計 – Web APIアーキテクチャ・データ連携実践ガイド

境界線の設計学:OAuth 2.0のトークン戦略と、TLSハンドシェイクの裏側で起きていること

ネットワークエンジニアの端くれとして、Web APIの設計を眺めていると、時折「なぜその設計にしたのか」という背景に、アーキテクトの苦悩と歴史的な教訓が透けて見えることがあります。特にアクセストークンとリフレッシュトークンの分離は、単なる認証の仕組みではなく、「信頼の寿命」をどう制御するかという極めてインフラ的な課題です。

今日は、教科書的な「短命なアクセストークン、長命なリフレッシュトークン」という定型句を、パケットレベルの挙動とセキュリティの最前線の視点から解剖してみましょう。

—

1. なぜ「短命」でなければならないのか:パケットの視点

アクセストークンが流出した際、攻撃者はそのトークンが有効な限り、あなたのリソースサーバーに対して特権的なアクセスを繰り返します。これを防ぐために「短命化(例えば5〜15分)」を行うわけですが、これは単に「早く消える」以上の意味を持ちます。

TLSハンドシェイクとRTTのジレンマ

アクセストークンを短くすれば、必然的にリフレッシュトークンを使ったトークン再発行(refresh_token grant)の頻度が増えます。ここで注意すべきは、トークン取得のたびに発生するTCP/TLSハンドシェイクのコストです。

もし、クライアントが毎回新しいコネクションを張っているなら、SYN -> SYN/ACK -> ACK の3ウェイハンドシェイクに加えて、TLS 1.3の1-RTTハンドシェイクが重くのしかかります。
これを防ぐには、クライアント側での Keep-Alive 設定と、サーバー側の TCP_NODELAY の最適化が不可欠です。

# Linuxカーネルパラメータ: 接続の再利用性を高めるためのチューニング例
# TIME_WAIT状態のソケットを再利用しやすくする
sysctl -w net.ipv4.tcp_tw_reuse=1
# 送信バッファの動的チューニング(高負荷時のTCP窓サイズ制御)
sysctl -w net.ipv4.tcp_wmem="4096 16384 4194304"

—

2. リフレッシュトークンによる「連鎖」とセキュリティの境界

リフレッシュトークンは、アクセストークンを再生成するための「マスターキー」に近い存在です。ここでの設計の肝は、リフレッシュトークンローテーションを実装することです。

攻撃者とのいたちごっこを終わらせる

リフレッシュトークンを使うたびに、新しいリフレッシュトークンを払い出し、古いものを無効化する。これを怠ると、リフレッシュトークンが一度でも漏洩した場合、バックエンドはそれが正当なユーザーなのか攻撃者なのかを区別できません。

さらに、もし攻撃者が古いトークンを再利用しようとした場合、「リフレッシュトークンの使用履歴」をDBに保持し、直ちに該当するユーザーセッション全体を強制終了するロジックを組むべきです。

# 疑似コード:リフレッシュトークンローテーションの概念
def refresh_access_token(old_refresh_token):
    # DBでトークンの有効性を確認
    token_entry = db.get_token(old_refresh_token)
    
    if not token_entry:
        # 既に古いトークンが使われた可能性がある(リプレイ攻撃の兆候)
        # このユーザーの全セッションを無効化する重大なセキュリティイベント
        security_audit.alert("Detect potential token reuse!")
        revoke_all_sessions(user_id)
        raise AuthException("Security violation")
        
    # 新しいペアを発行し、古いものを即時破棄
    new_access, new_refresh = issue_new_tokens()
    db.delete_token(old_refresh_token)
    db.save_token(new_refresh)
    return new_access, new_refresh

—

3. パフォーマンスを殺さないためのヘッダー設計

トークンを扱う際、HTTPヘッダーの肥大化は無視できない問題です。特に Authorization: Bearer <JWT> のような形式をとる場合、JWTのサイズが数キロバイトに達することがあります。

HPACKによるヘッダー圧縮の恩恵

HTTP/2を利用している場合、HPACK 圧縮が働きますが、JWTのように値が毎回変わるものは圧縮効率が落ちます。

  • 対策: 不要なクレームをJWTに入れない。必要な情報はサーバー側でRedis等の高速なKVSにキャッシュし、JWTには sub (Subject) と jti (JWT ID) 程度を載せるのが、インフラアーキテクトとしての「美学」です。

—

4. インフラ担当者が守るべき「ラストライン」

最後に、ネットワークスペシャリストとして強調したいのは、TLSの終端における防御です。どれほど完璧なトークン設計をしても、TLS 1.2以前の古い暗号スイート(CBC モードなど)を許可していれば、プロトコルレベルの脆弱性で台無しになります。

必ず以下の設定を適用してください:
1. TLS 1.3の強制: AES_128_GCM 以上の認証付き暗号のみを許可する。
2. HSTS (HTTP Strict Transport Security): ブラウザに対して Strict-Transport-Security: max-age=63072000; includeSubDomains; preload を強制し、中間者攻撃(MitM)を完全に排除する。
3. IPバインディングの検討: もしモバイルアプリのような固定的なクライアントであれば、セッションとIPアドレスやデバイスフィンガープリントの紐付けを検討する価値があります(ただし、CGNAT配下のユーザーには注意が必要)。

結びとして

API設計とは、コードを書くことではありません。「データがどの経路を通って、どこで検証され、どう破棄されるか」という、パケットの旅路を設計することに他なりません。

トークンの有効期限を短く設定するということは、サーバーに負荷を強いることになります。しかし、そのトレードオフの先にあるのは、攻撃者が侵入した瞬間に価値を失う、極めて堅牢で美しいシステムです。皆さんの手元にあるAPIが、今日も安全にパケットを届けていることを願っています。

コメント

タイトルとURLをコピーしました