前回、JWTとともにSpring Securityでの実現方法についてポストを書きましたが、JWTを使ってAccess Tokenのみを発行した場合は、考えられる脆弱性があるらしいです。なので今回は、Access Tokenのみを使う認証の特徴と問題点及びそれを補完するための技術を紹介したいと思います。
そもそもJWTを使った認証認可は、JWTがISO標準ではあるものの、正解と言った実装方式がないらしいです。なので以下で紹介するAccess Tokenのみの発行に伴う問題と、それを補完するための戦略は概念だけを紹介して行きます。
Access Token
Access Tokenのみを使う場合は、最初の認証以降、サーバでは秘密キーを利用してリクエストに載せられたTokenを確認するため、データベースやストレージなどのI/Oが必要なくなるため高速で処理ができます。ただ、Tokenはリクエストを送るクライアント側が持っているため、強制的にセッションを終了させることができません。なので、ログアウトの処理をするためにはクライアント側からToken情報を削除するしかないですね。
Access Tokenを使った認証方式はこういう特徴を持っているため、クライアント側がTokenを持っているため、これをもし第三者に乗っ取られた時はサーバサイドからそれを判断できる術がありません。こういう問題を防ぐため、Tokenを発行する際はあらかじめ期限を設定することで、一定の時間がすぎるとTokenの有効期間が満了するようにして、再利用できなくしています。
Tokenの期限設定のジレンマ
Tokenの期限をあらかじめ設定する場合、有効期限をどう設定するかの問題があります。短くした場合は、Tokenを乗っ取られた場合でもすぐにTokenを使えなくなるため安全性を担保できますが、逆にユーザは頻繁にログインし直さないと行けなくなります。逆に長くした場合は、ユーザはログインし直さなくてもサービスを長期間利用できるものの、乗っ取られたTokenも長く使えるようになりますね。
Refresh Token
Refresh Tokenは、満了したAccess Tokenの有効期限を更新するための別のTokenです。サーバは認証時にクライアント側にAccess TokenとRefresh Tokenをともに発行します。ここでAccess Tokenの期限は30分くらいの短い期間に設定して、Refresh Tokenは1ヶ月ほどの長い期間に設定します。
クライアント側でログイン後に30分がすぎて、Access Tokenの期限が満了した場合にRefresh Tokenでサーバに新しいAccess Tokenの発行を依頼します。サーバはクライアントのリクエストに乗ってあるRefresh Tokenを検証し、問題なければAccess Tokenを再発行します。これでAccess Tokenを乗っ取られたも、すぐに満了し、ユーザはRefresh Tokenを持って有効期限の自動更新ができるので毎回ログインする必要がなくなります。また、サーバサイドでRefresh Tokenを検証するデータを持っているので、もし問題がある場合はRefresh Tokenを満了させることもできます。
ただ、Refresh Tokenを使った場合でも完璧では言えません。サーバが検証するためにRefresh Tokenに対応するデータを別のデータベースやストレージに保存する必要があるので、追加的なI/Oが発生し得るという問題はあります。また、Refresh Tokenを用いたAccess Tokenの更新のためにはサーバとクライアント側の両方に機能を追加する必要がありますね。あと、Access Tokenと同様にクライアント側がRefresh Tokenを保存する必要があるのでこれをどう保存するかを考える必要があります。
Sliding Session
Sliding Sessionは、セッションを継続して利用しているユーザがいる場合、自動でAccess Tokenを更新してくれる仕組みのことです。リクエストごとに新しいAccess Tokenを発行する方法がありますが、特定のリクエストに限って処理する方法もあります。例えば、ユーザが長い時間を送ると予想されるページ(問い合わせの作成)や、次に何かのアクションが予想されるページ(ショッピングカートにアイテムを追加する)の場合があるでしょう。他には、Access TokenのIAT(トークン発行時間)を確認して新しいAccess Tokenの発行を要請する場合もあるらしいですね。
ただ、Sliding SessionでAccess Tokenを更新する場合、そもそもの有効期限が長く設定されていると無限に近くセッションが延長され、ユーザが全くログインしなおさなくてもサービスを利用できることになるので注意が必要です。逆に、ユーザがあまり長い時間の作業を行わない場合はSliding Sessionそのものの必要性がなくなる可能性もあります。
Refresh Token + Sliding Session
最後は、Refresh TokenとSliding Sessionを同時に使った方法です。普通のSliding Sessionを適用した方法との違いは、Access TokenではなくRefresh Tokenを更新するということです。Refresh Tokenを更新することにより、Access Tokenを頻繁に更新する必要がなくなります。またRefresh Tokenが更新されるためアクセス回数の少ないユーザでも比較的ログインを頻繁にする必要がなくなりますね。Refresh TokenとSliding Sessionのメリットを適切に融合させた戦略と言えます。
ただ、この方式でも問題が全くないとは言えません。サーバからRefresh Tokenの有効期限を満了させない限り、ユーザはリソースにアクセスできるので、もしPCやスマホなどの機器が乗っ取られた場合は対応できません。なのでアカウントのパスワードを周期的にリセットさせるか、特定のリソースにアクセスする場合は再び認証させるなどの方法が必要となります。
最後に
認証はWebアプリケーションにとってもっとも重要で、当たり前な機能となっていますが、セキュリティのため強化して行くとユーザの使用性に悪影響が出てしまうという問題を持っているものです。なので色々な方法論があり、さまざまなWebアプリケーションではそれぞれ違う方法を使って脆弱性に対応していますが、どれもメリット・デメリットがあるので正解と言える物はないのかもしれません。
また、JWTのPayloadは暗号化されないため(ただのbase64文字列)、JWTを乗っ取られた時にその中身を見れるという問題もあるらしいです。これを補完するためにPASETOという物も提案されているらしいですが、これがまたJWTほど亜補給される稼働かはわからない状態ですね。
あと、そもそもの認証方式の問題ですが、量子コンピュータが一般に補給されたら、従来の方式による暗号の解析はすぐにできるようになるとも言われているので、暗号化という考え方そのものが変わる可能性もあるかもしれません。そうなると既存のアプリも全体を作り直さなくてはならないことになるでしょう。
セキュリティは難しいものです。暗号化とともに、ユーザが便利に使える設計、戦略を適切に選定するためには色々と工夫が必要と感じます。