世の中にはJWT(JOSE/JWS/JWE)でセッション管理をしてはいけないという記事が2017年から山ほどあるのに、なぜかJWTでセッション管理をしようとする人がいる。翻訳記事だったり暗号の説明が長すぎたりして、JWTをセッションに使ってしまうような人の心に刺さってないんじゃないだろうか。
前提
JWTでセッション管理というのは、暗号化したトークンをブラウザのCookieに持たせて、サーバー側ではトークンを復号化してユーザー判定などのセッション管理を行うことである。サーバー側で[sessionId: userId]のペアを管理する必要がないのでステートレスに扱えてスケールしやすいというメリットがある。
問題
すごく便利な図があったのでまずこれを読んで欲しい。セッション方式の策定/設計をする職位の人ならすんなり読めると思う。
左から4番目Local Storageは読んでそのままこれ以上補足することもない。残りの部分は下記の2点に集約される。
明示的にログアウトするにはサーバー側の秘密鍵の変更が必要
このため次の二択を迫られる。
- 誰か一人でもログアウトしたいときは全員ログアウトになる(秘密鍵を更新した場合)
- なりすましログインが発覚しても、トークンの有効期限が来るまではそのセッションは無効にできない(秘密鍵を更新せずに耐える場合)
これが許されるのは金銭の絡まないサービスか、ユーザーに損害があっても運営がポイントを配れば済む(詫び石対応)類のサービスなどに限られるだろう。でもセキュリティは固いにこしたことはない。仮想通貨取引サイトで後者を選択したら、犯人が外部に送金していくログを指を咥えて見ているしかない。
従来のセッション管理の方がマシ
前項の不便さを回避するために、次のような回避策を考えるかもしれない。
- 明示的にログアウトしたユーザーを無効なトークンとして管理する
- ユーザーがパスワードを変えたらトークンも再発行の必要があるようにする
結局のところサーバーサイドでのステートフルなセッション管理であり、JWTのメリットであるステートレスを捨てている。新規に実装するよりも、従来の枯れているセッション管理実装を使ったほうがわかりやすくバグの見落としも少ないだろう。
認証
セッション管理ではなくOAuth認証などに使うのは問題ない。一回認証したら終わりなので、有効期限を短くしておけば済む。「一人のために全員ログアウト」のような不便は発生しない。
所感
一部実装がalgヘッダにnone(暗号化なし)を許可していただとか、どの暗号化方式にどんな弱点がとか、話題が分散するとどこが重要なのかわかりにくくなる。RFCを読めなどと言えば拒否反応を示す人もいるのである。正しく手短に伝えるのは難しいですね。
参考URL
- No Way, JOSE! Javascript Object Signing and Encryption is a Bad Standard That Everyone Should Avoid (PARAGON INITIATIVE, 2017-03-14)
- JOSE(JavaScriptオブジェクトへの署名と暗号化)は、絶対に避けるべき悪い標準規格である (POSTD, 上記PARAGON記事の翻訳, 2017-04-13)
- どうしてリスクアセスメントせずに JWT をセッションに使っちゃうわけ? (co3k.org, 2018-09-20)
- JWTをセッション管理に転用するのはあまり良いアイデアではない(認証だけならいいよ) (anatooのブログ, 2018-10-03)