🦁

"JWT=ステートレス"から一歩踏み出すための考え方

3 min read

おはようございます、ritouです。

この話に乗っかっていきます。

3行で

  • ログアウト時にJWTを無効化できない実装は今後脆弱性診断で「OWASP Top 10 2021違反」と指摘されるようになりそう(今も個別にされてるかもしれないけど)
  • JWTは単純なフォーマットなので、ステートレスなセッション管理においてログアウトしたときに文字列自体を無効化できない件は独自エンコード方式(一般的にフレームワークのCookieストアと呼ばれているもの)でも起こり得る
  • 「セッションID vs JWTで内包」 以外にも 「セッションIDをJWTに内包」もあり得る。既存の機能を残しつつ「JWTで武装」する選択肢も考えてみてはどうか。

ステートレスなセッション管理でログアウトの際に文字列自体を無効化できない問題

これは前から言われていますし、駆け出し何とか勢のQiita記事に書かれるぐらいには一般的です。

2021年にもなって SPAならJWT ぐらいにしか思ってない人には何もいうことはございませんが、JWTはあくまで署名や暗号化可能なエンコードフォーマットなのでこの問題を整理すると、

  • まぁ昔からCookieストア方式のセッション管理ってのは存在するし特徴を知っててリスク受容できていれば問題ないだろう
  • アクセス頻度などをチェックして定期的に有効期限を適切に伸ばしたJWTを発行していくなど、リスクを減らすワークアラウンドもある
  • JWTでステートフル言ってるうるせぇ猫野郎がいるな

みたいな認識だったところが、

  • ログアウト時にJWTを無効化できない奴は何をやってもダメ

ってなってザワついちゃうんじゃないかいうお話です。

今回の話は "Cookieストア" な仕組み全般に言えるかもしれない

この問題を 無駄に大きく捉えると、 JWTだから!って話ではなく、Webアプリケーションの "Cookieストア" 相当の実装でログアウト時にHTTP Cookieを削除するだけ の実装も対象に含まれるのではないかとも考えています。

  • ログアウト機能はあるが直前に利用できていたセッションCookieの値が再利用できる

みたいな実装になっていたら、原理としては無効化できないJWTと一緒です。

ちなみに、JWTを無効化する方法として、徳丸さん、川崎さんTweetにある通り、

  • 無効化対象のjtiを管理しておく

みたいなアイディアは昔からあります。

OAuth 2.0のAccess TokenへのJSON Web Token(JSON Web Signature)の適用 - r-weblife

(この記事は2014年のものですが、それより前の有識者の方の記事を参考にしたものなので、アイディア自体はとっても古くからあります。)
とはいえ、、 そこまでするならJWT使わないだろwww というのがこれまでの多数の意見なのではないでしょうか。

「セッションIDをJWTに内包」という考え方

いわゆる「セッションID vs セッションデータ内包」なイメージができている場合、今回の話を受けて、 普通にセッション管理でいいじゃん となる人もいるでしょう。

この意見に異論はありません。

SPAでもこのいわゆるセッションIDの値を何とかストレージとかお好きなものに保存してやっていけば良いのです。
その形式のリクエストを処理するミドルウェアがないのであれば、そのフレームワークでは今まで想定されていなかった、それだけのことでしょう

しかし、個人的には、ここから一歩踏み出した考えを持っていただく絶好の機会だと思うので少し追記します。
「セッションIDをJWTに内包する」 という考え方です。
セッションIDを内包したJWTをセッションCookieとして利用することで

  • セッションIDを用いたセッション管理機能はそのまま利用できる
  • 署名の仕組みを用いて改竄検知が可能
  • 内部でセッションIDを無効化することでJWT自体も無効化可能
  • JWTの有効期限を更新していくことで「一定期間で無効になる文字列」をゆるく実装可能

という状態になります。
私はこれを「JWTで武装」のように表現することがあります。

後述する資料にて、OAuth 2.0のアクセストークンへのJWT適用例の一つとしてハイブリッド型アクセストークンについての記事を紹介したりしています。
単一のWebアプリケーションのセッション管理を強化、なんて言ってもそんなに厳しい要件ないから良いや、ぐらいに思われるかもしれませんが、これが複数のリクエストを繋げて実現するmicroservices的な構成であったらどうでしょうか。

  • 明らかに無効な文字列 "hogehoge"
  • JWTのPayloadにあるセッションIDを改竄したもの
  • 有効期限が切れたJWT文字列

などを、データストアを参照する前に弾くことができるので、無駄なリクエストを減らすこともできるでしょう。
もちろん要件に対してオーバースペックな実装は不要だと思いますが、この辺りの感覚を持っていることで今後の設計/実装に活かすこともできるでしょう。

参考資料

今回の話含めてJWTの活用について、この記事にまとめています。
良かったら読んでみてください。

2020年版 チーム内勉強会資料その1 : JSON Web Token - r-weblife

そして、このCookieとの比較でもよく言及される点として、「JWT=ステートレスでなければならない」「JWTを使う場合は必要な情報を詰め込み、その内容だけで機能を実現させる必要がある」という一種の信仰のようなものがあるのではないかと考えています。
これはとても勿体無いなと感じていて、確かに情報を内包できるという特性は持っていますが、データストアなどを参照するためのキーを持ってはいけないというわけではありません。
例えば単純な識別子を用いたやりとりだったものにJWTを適用するだけで、有効期限や検証という機能を追加できる のです。この考えは既に色々なユースケースで使われているものではありますが、ステートフルなユースケースへも適用できるということを今一度意識してもらえると良いのかなと思っています。

ではまた!

この記事に贈られたバッジ

Discussion

ログインするとコメントできます