サーバサイドでJWTの即時無効化機能を持っていないサービスは脆弱なのか?

きっかけ

昨年(2021年9月ごろ)に徳丸さんのこのツイートを見て、「2022年にはJWTを用いたセッション管理に代表される、ステートレスなセッション管理は世の中に受け入れられなくなっていくのだろうか?」と思っていました。

結論

冒頭だけ読んで勘違いが広まるとよくないので、議論の結果最新版では方針が変わったということだけ先に書いておきます。詳細が気になる方は最後まで読んでみてください🙇‍♂️

現状(2022年3月)の日本語版の記述

OWASP Top10 2022の「A01:2021 – アクセス制御の不備」の日本語版では、2022年3月3日時点では以下のように書かれています。

JWTトークンはログアウト後にはサーバー上で無効とされるべきである。

github.com

この記述からは、ログアウト時にクライアントサイドでセッション(JWT)を捨てるだけでは不十分で、クライアント側がJWTを再利用すればサーバへのアクセスが成功してしまうのが問題であるということが読み取れます。 サーバ側で無効なJWTを管理しなければならないということは、JWTを利用したセッション管理に代表されるようなステートレスなセッション管理は認証の実装方法として不十分なのでしょうか?

議論

こちらのIssueで議論がされていました。(徳丸さんのツイートよりも後に行われた議論のようです)

github.com

議論の詳細はここでは書きませんが、ここの議論からわかることをまとめます。

まず、JWT仕様を見る限り、OAuthをベースとしたOpenID Connectで使用することが想定された設計であることは明らかです。

datatracker.ietf.org

OAuthには2つのトークンがあり、短命なアクセストークンと長命なリフレッシュトークンから構成されています。 それぞれのトークンがどんな形式であるべきかは指定されていませんが、アクセストークンにはJWTが使われている実装が多いようです。

アクセストークンの生存期間を短命にすることで、アクセストークンが不正に利用されるリスクを低減しています(リスクを低減しているのであって、完全に防いでいるわけではない)。この短命なトークンの実装としてJWTを使うことで、ステートレスな認証を実装することができ、データベースの負荷低減に寄与します。

その代わりにリフレッシュトークンはサーバサイドでの管理を必須とし、ユーザがログアウトするなどの操作を行うとリフレッシュトークンが即時無効化され、アクセストークンの更新はできなくなります。

この前提があるため、少なくとも長命なアクセストークンを生成することは許容されなさそうです。どれくらいの時間が短命なのかどうかはサービスごとのリスク許容度によって判断する形になりそうですね。

(追記ここから 2022/04/18) 徳丸さんから反応をいただいたのですが、

たしかに誤解が広がるとよくないので、こちらでも強調しておきます。 JWTは本来的にはセッション管理のためのものではなく認証情報の受け渡しのためのものですが、その便利な特性からセッション管理にも転用されることもあるという背景があります。

こちらも参考になったので紹介しておきます 🙇‍♂️

(追記ここまで)

「数分であろうと即時無効化されているべきであるセッションが有効なものとして使えるのはリスクは低減されているが問題であることに変わりはないのでは?」という意見ももちろんありますが、絶対に許容されないという温度感ではないようです。

その代わりに、この特性が一般にはまだ広く理解が広まっていないのでは?という懸念への対応として、以下のリンクを紹介することでメリットとデメリットを正しく理解してもらおう、ということで着地したようです。 www.oauth.com

変更

この議論の結果、2021年9月24日にはA01の記述を修正するプルリクエストが出おり、即日取り込まれていたのでした。

github.com

Stateful session identifiers should be invalidated on the server after logout. Stateless JWT tokens should rather be short-lived so that the window of opportunity for an attacker is minimized. For longer lived JWTs it's highy recommended to follow the OAUTH standards to revoke access.

ということで、

「ステートフルなセッション識別子は、ログアウト後にはサーバー上で無効とされるべきである。ステートレスなJWTトークンは、攻撃者の機会を最小にするために、むしろ短命であるべきである。寿命の長いJWTの場合は、アクセスを取り消すためにOAuth標準に従うことが強く推奨される。」

という内容に記述が変わりました。

日本語版が追いついていない😱

日本語版の記述はどうなっているかというと、この議論の結果が反映されておらず、英語版と日本語版で乖離が出ていることがわかりました。 日本語版だけを読んで勘違いが広がってしまうのもよくないので、英語版の最新に追従するプルリクエストを出してみました。問題がなければ取り込んでもらえるかな?と思っています。

github.com

JWTの取り扱いに関する記述が意味的な差分としては一番大きそうですが、他の差分に関しては軽微な修正だと思われます。