JWT 認証のメリットとセキュリティトレードオフの私感

2020/5/9追記: 考えた結果、Authorization Bearer ヘッダを使った正規のJWTの場合、同一ドメイン下で読み込む全 JavaScript が信用できる場合でないとブラウザ上で安全にトークンを保持できないのでブラウザからのAPIアクセス時の認証用には使うべきではないというところに着陸しました。ブラウザからのアクセスでは http only cookieトークンを入れ、 CSRF 対策も忘れずにというこれまで通りの定石が手堅いように思います。 JWTを使うのはトークンの安全な保管ができる非ブラウザなネイティブクライアントからのAPIアクセス時に限った方がよさそうです。 APIサーバ側ではアクセス元に合わせて認証方法を使い分ける両対応が要求されるので手間は増えますが手抜きできる場所でもないので仕方なしと。


2018/9/25追記: https://gist.github.com/issm/63889b931b8c658f23634070b64f8b23 も参考になるかも。 あと、以下の議論は『セッション』の意味するところに認識違いがあるのかもしれない(認証継続の意味でのセッションと、ステート保持機構としてのセッションと)。


どうして JWT をセッションに使っちゃうわけ? - co3k.org (←のはてブ
とか
JWT認証、便利やん? - ブログ (←のはてブ
で話題になってるので、Webシステム素人の理解と私感をメモしてみる。
OpenID connect とかで使われてるって話だけど、そっちはノータッチで単純にAPIアクセス時の認証の仕組みとして使うことを前提としています。


JWT ベースの認証って、負荷分散などのために複数台のフロントサーバを使う場合や、複数のマイクロサービスを使ったサービスを実現するにあたって、認証(セッション)状態を都度セッションストアに確認したくない・できない場合に生きてくる仕組みなように思う。

一般に短寿命のアクセストークンと比較的長寿命なリフレッシュトークンのセット利用を前提としていて、次のような利用形態が前提(なはず)。

  • 普段の APIリクエスト時はアクセストークンのみで認証する。
  • アクセストークンの寿命が来た場合には、リフレッシュトークン使ってアクセストークンを更新する。
  • そのリフレッシュ時には都度ユーザDBなりにアクセスしてリフレッシュ可否を判定する。
  • APIリクエスト頻度 <<< アクセストークンのリフレッシュ頻度である。


従来の SessionID を使ったトークンベース・cookieベースの認証では、SessionID からユーザID などの認証情報を引かないといけないので、その対応関係(セッション情報)を一時保存するセッションストアが必要になる。

API リクエストが高頻度に発生する場合では、複数台のフロントサーバで分散させる構成にするのが一般的だけれども、セッション情報は全体で共有しないと破綻するので、 memcached や Redis のような共有セッションストアサーバを別途用意して、フロントサーバ全体で共有しなければならなかった。


それに対して JWT だと普段の APIリクエストに対しては、各フロントサーバで JWT から認証情報を取り出せるので、共有セッションストアサーバが不要になる。(そこで改竄検知可能なのが JWT のキモだと思う)

リフレッシュの頻度は APIリクエストに対して十分に低頻度で、バックエンドの普通の DB サーバで十分に対処できる。
このため、リフレッシュが要求された時点で DB を参照してアクセストークンをリフレッシュする/拒否することで認証状態をコントロールする。


トークンの漏洩に対しては、漏洩発覚後、アクセストークンの寿命が来るまでは不正アクセスを許容できるサービスが適用対象で、即時止めたいというサービスには向かない。
そういうサービスの場合は、セッションストアサーバを使って確実にセッション無効化できるように構築しましょう。
無理に JWT で実現しようとしても、blacklist をフロントサーバ間で共有させないといけないので結局共有セッションストアのような仕組みが必要になってしまうので。


共有セッションストアサーバが不要になるというのが、JWT 認証での一番のメリットで、あとはアクセストークンの寿命の設定次第で、許容するリスクとリフレッシュ負荷とのトレードオフを調整する。


この方面は素人なんで要点外したら申し訳ないけれど、調べた範囲だとこういう感じなのかなぁと。

自前サービスのAPI認証用途で使う場合であれば、ユーザの無効化操作と同時にフロントサーバ群にそのユーザ情報を管理APIなどで上手くばらまく仕組みができれば即時無効化もできるはず。
無効化情報はせいぜいアクセストークンの寿命時間分保持すれば十分なので、再起動を考えなければオンメモリでもいけそうな。


各種フレームワークで用意されてる・対応してるセキュアな実装が使えるなら、それに乗っかるのが一番無難な選択よね。

あ、マサカリはウレタンでお願いします。