Webアプリの認証にJWTを使っても良いのか
結論から言いますと、JWTは認証に使っても問題無いです。
ただし、トークンの保管方法やXSS対策に気を付けなければいけません。(これはセッションIDだろうがなんだろうが一緒です)
また、「トークンが無効化できない」という話がありますが、トークンを無効化する方法はあります。
どういうことかを説明していきたいと思います。
JWTをざっくり説明
JWTを知らない方のために先に説明します。
JWTとはJSON Web Tokenの略で、「ジョット」と発音します。
認証に使える仕組みとしてはシンプルで、JWTには"署名"が含まれるため、発行した側は「自分が発行し、改ざんされていないこと」を検証でき、ログインに成功したユーザーにJWTを発行して通行証のようにできるためです。
もっと詳しく知りたい方は、「公開鍵暗号[1]」でまず検索し、理解したらJWTのWikipediaを読んでください。
トークンの保管方法
JWTやセッションIDなどの「他者に知られてはいけないトークン」の保管方法を説明します。(これはJWTに限った話ではないです)
まず、ブラウザを利用したWebアプリの場合、トークンはCookieに入れます。
CookieはHttpOnly
とSecure
、そしてSameSite
の属性を設定します。
-
HttpOnly
・・・document.cookie
などを介したアクセスができなくなります。ただし、画面遷移時やfetchなどのHTTPリクエスト時には送信されます。XSS対策の一つです。 -
Secure
・・・HTTPS通信でしかCookieがやりとりされなくなります。トークンの漏洩・改ざん対策です。 -
SameSite
・・・Strict
に設定
HttpOnly
はXSS対策の一つです。
悪意のあるコードが紛れてしまった時=XSSが発生した時、攻撃者は自分のサーバーにトークン等の情報を送信しようとするわけですが、JSで読み取れないためトークンを送信できなくなります。
SameSite=Strict
は別ドメインにCookieを一切送信しない設定です。
これでCookieに関しては問題無いですが、結局XSSが発生するとトークンは遅れなくても、他の情報は送れてしまいます。
その対策に「同一オリジンポリシー」というものをブラウザは備えているのですが、この記事から脱線するので説明しません。
詳しくは、MSNのCORS記事を読んでください。
トークンの無効化
ただ、この方法だとすべてのトークンが無効になり、全員が再ログインしないといけなくなります。
特定のJWTトークンだけを無効化する方法はありません。
まず「トークンの無効化」はどういった場合に必要になるでしょう?
例えば、
- クラッキングにより、データが漏洩した場合
- 特定のユーザーから、パスワードなどが盗まれたと連絡があった場合
などでしょう。
1.の場合は全てのトークンを無効化し、ユーザーにパスワード変更と再ログインを促すことになるため、JWTでも対応可能です。
一括でJWTのトークンの無効化するのは簡単で、署名に使っている公開鍵と秘密鍵のペアを変更すればいいだけです。
では2.の場合はどうすればよいでしょう?
まず、認証方式としてJWTを使う場合、ペイロード内にはユーザーIDを入れておくでしょう。
そして、ユーザーIDを基に、ユーザー情報をDBに問い合わせます。
ここで、「ユーザーが無効化されていれば、機能を利用させない」ようにすれば、実質的にJWTを無効化するのと同等の効果が得られます。
つまり、準備が必要ですが、「ユーザーID」や「有効期限」などの属性に基づいた無効化(と同等の挙動)は可能ということです。
JWTは安全なのか
JWTの安全性は、JWTに署名を付ける際のアルゴリズムの安全性次第になります。
アルゴリズム自体はなんでもいいのですが、量子耐性があるようなアルゴリズムを選べば問題ないでしょう。
JWTのライブラリはいくつかオプションを用意しているはずなので、それぞれ調べて選択してください。
ただし、「指定しない場合」の挙動がセキュアではないライブラリもあるので、必ず指定するか、デフォルトの挙動を把握しておくことをオススメします。
JWTは速いのか
JWTの署名(ログイン時)と検証(認証時)にどれだけ時間がかかるかによってパフォーマンスは決定されます。
これも結局はアルゴリズム次第になってしまいます。
個人的には「署名は遅くても、検証が早い」アルゴリズムを選ぶことをオススメします。
これもまた、ライブラリが用意しているオプションから安全性を加味して選んでください。
とはいえ認証にJWTを使うのか
実際のところ、サーバーセッションを利用した認証ができるなら、自分は迷わずそちらを使います。
ただ、その場合は何かしらのストレージサービスが必要になるため、そういったサービスが利用できない場合はJWTは選択肢の一つになります。
また、最初はJWTを利用し、サーバーセッションに移行することも可能です。
社内用や個人用、初期開発フェーズでは非常に便利でしょう。
少なくとも「認証にJWTは絶対にNG」なんてことは全くないと思います。
ではJWTは使わないか
認証で使わないとしてもJWTは素晴らしく便利です。
例えば、クーポン用のコードを配布した時、JWTで署名しておけば安心でしょう。公開鍵暗号であれば、任意の他者による検証も可能です。
要は「署名ができて、JSONのデータを格納できるトークン」なのですから、その規格が役に立つなら、目的や用途はなんだっていいのです。
技術全般に言えることですが、仕組みを理解し、活用できる場面で適用することはエンジニアの役割そのものです。
JWTもその仕組みを理解した上で、活用していけばよいと思います。
-
JWTの署名アルゴリズムは必ずしも公開鍵暗号である必要は無いですが、仕組みの理解として良い題材です ↩︎
Discussion