こんにちは!seiです!
webアプリケーションのセキュリティ観点として
・認証、認可によるリソースの制御
・サイバー攻撃(XSSやSQLインジェクション等)の対策
の二つがあると思います。
今回はJWTトークンによる認証と、セッショントークンによる認証についてそれぞれのメリット、デメリットがよくわからなかったので備忘録です。
JWTトークンとセッションによる認証の違い
セッション認証の場合、ユーザ情報とセッションIDを対応させた情報をサーバー側が持っている必要があります。
一方、JWTトークン認証の場合はトークンとユーザ情報を対応させた情報を持つ必要はなく、トークンに含まれるユーザID等で認可(リソースの制限)を行います。ユーザIDを改ざんすると検知できるので、トークンに含まれる情報を用いてOKです。
JWTトークン認証のメリット、デメリット
- サーバーに情報を保存する必要がない
- ユーザが増えてもスケールしやすい
- トークンを無効化することができない
セッション認証のメリット、デメリット
- トークンを無効化させることができる
- サーバに情報を保存しないといけない
- ユーザが増えるとスケールしにくい
JWTトークン認証の場合は、サーバーに対応関係を持っていないので、一度発行したトークンは無効化することができません。
なので、有効期限はできるだけ短くしたほうがよさそうです。
googleやfacebook等ユーザーがめちゃくちゃ多いサービスはJWTの使用頻度が多そうです。
HTTPのリクエストに含まれる際の違い
HTTPメソッドを使ってリクエストを送るアプリケーション(RESTAPI)を構築している場合は、
セッションはHTTPヘッダーのCookieヘッダーに
JWTはHTTPヘッダーのAuthorizationヘッダーに含むのが一般的です。
JWTトークンによる認証の仕組み
JWTトークン(Json Web Token)とはオフラインで認証することができるトークンです。改ざんすると検知することができるので、改ざんは不可能です。(
JWTトークンの構造はピリオドで以下3つの部位に区切られています。
ヘッダー.ペイロード.署名
実際には以下のような文字列です。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.DsO_2b8myX8xvBhG-F6sIc2uVpO1zPqT1ZyXe1jXsSg
このうち特に重要なのは、ペイロードと署名なので、絞って説明します。
ペイロード
ペイロードとはトークンの本体みたいな部分です。有効期限やユーザのid、emailを含むことができます。この情報を改ざんすることは不可能なので、webアプリの認証に用いられることが多いです。
ただペイロードはBase64でエンコードしているだけで、中身の情報はだれでも見ることができてしまうので、見られると困る情報は入れないようにしましょう。
署名
署名はトークンを実際に認証する際に使う部分です。
トークンを作成する際に、ヘッダー、ペイロードを用いてハッシュ値を作成し、秘密鍵を用いて署名部分を作成します。(暗号作成)
トークンを認証する(トークンが有効なものかチェックする)際に、署名部分を公開鍵を使って複合しハッシュ値を複合します。さらに、ヘッダーとペイロードを用いてハッシュ値を作成します。
この複合によって得たハッシュ値と、作成によって得たハッシュ値に差異がある場合、そのトークンは改ざんされていることになります。
セッション認証の仕組み
一方セッション認証では、ユーザがログインしたときに、そのユーザ用に「セッションID」を作成してDBなどに保存します。(ステートフル)
ログイン以降のリクエストはこの「セッションID」が含まれているので、これをもとに認可リソースの制御を行います。(どのユーザにどのリソースが見れる権限があるのか制御)
セッションベース認証の流れ
- ログイン:
- ユーザーがユーザー名とパスワードを使ってログインします。
- サーバーは認証情報を検証し、正しい場合は新しいセッションIDを生成します。
- セッションIDの送信:
- 生成されたセッションIDは、HTTPレスポンスの一部としてクライアントに送信されます。一般に、これはセキュアなHTTPクッキーに格納されます。
- セッションIDの保存:
- クライアント(ブラウザ)はこのセッションIDを保存し、以降のリクエストにこのIDを含めてサーバーに送信します。
- セッション追跡:
- サーバーは受信したセッションIDを使って、対応するセッション情報を検索し、ユーザーの認証状態と関連データ(例えば、ユーザーの設定、権限など)を維持します。
- セッションの維持と終了:
- サーバーはセッションIDを使って、ユーザーの状態を複数のリクエストにわたって維持します。
- ユーザーがログアウトするか、一定期間アクティブでない場合、セッションは終了し、IDは無効になります。
JWTのデメリットを緩和するには?
有効期限を短く設定する
当たり前ですが、有効期限が長ければ長いほどトークン漏洩の可能性は高くなります。
ただ頻繁にアクセストークン切れでログインを求められると、ユーザビリティが悪くなるので、リフレッシュトークン(アクセストークンよりも有効期限が長めのトークン)を使って、アクセストークン切れの際はトークンをリフレッシュするのが一般的です。
サーバーに情報を保存する
セッション認証との違いが薄れてしまいますが、サーバに情報を保存することも検討できます。
すでに認証が切れているトークンを保存するブラックリストを作成し、検証のタイミングでブラックリストのチェックも行うようにするなどの方法があるようです。
僕が実際のプロジェクトで行ったことのある方法は、Userテーブルにaccess_tokenカラムを追加して保存しておくものでした。一人のユーザに対して一つのトークンのホワイトリストを持つ構造です。ただし、この方法のデメリットは一人のユーザに対して、一つのJWTトークンしか許可しないので複数端末による同時ログインができません。パソコンでもスマホでも同一ユーザが同時にログインしたい場合は使えません。
また、開発用アカウントが一つしかない場合も、チームメンバーによる同時ログインができないので、開発用アカウントだけはDBとの照合を除外していました。
まとめ
認証の部分結構ややこしくて難しいですよね。
仕組みを理解できても、すぐに忘れてしまうので自分なりにまとめるのが大事かなと思います。
僕はセッション認証を用いたアプリケーションをLaravelで作成したことがありますが、Laravel側が勝手にいい感じにやってくれていたので、あまり意識していませんでした。Laravelだと、セッション情報はデフォルトではファイルに保存されるようです。
今回JWTとの違いがしっかり分かったので、今後の業務に活かしていこうと思います!