JWT (JSON Web Token)

JWT (JSON Web Token)は、認証・認可の情報をシステム間で安全にやり取りするための標準的な手法となります。

RFC 7519で定義されていて、そこの概要は以下の通りです。

JSON Web Token(JWT)は、2つのパーティ間で転送されるクレームを表す、コンパクトでURLセーフな手段です。JWTのクレームは、JSON Web Signature(JWS)構造のペイロードとして、またはJSON Web Encryption(JWE)構造のプレーンテキストとして使用されるJSONオブジェクトとしてエンコードされ、クレームにデジタル署名したり、メッセージ認証コード(MAC)で整合性を保護したり、暗号化したりすることを可能にします。

読み方

JWTは一般的に「ジョット(jot)」と読まれます。

RFC 7519で明確に定義されています。

The suggested pronunciation of JWT is the same as the English word “jot”.

一言でいうと

一言でいうと、「改ざんを防ぐ仕組みがついた、デジタルな証明書」のようなものです。

テーマパークの「入場・再入場のスタンプ」で例えると、

  1. 入場する際に、チケットを見せてスタンプを押してもらう。(JWT発行)
  2. アトラクションに乗るときや再入場するときに、スタンプを見せて確認してもらう。(JWT提示・検証)
  3. 自分でスタンプを消す(JWT破棄)か、時間が経ってスタンプが消える。(JWTの有効期限切れ) のような流れでJWTは利用されるイメージです。

JWTの仕組み

JWTは下記のような形式で表現されます。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOmZhbHNlLCJpYXQiOjE1MTYyMzkwMjJ9.HO95GJZFIR0Qoj2cor4jVbJNu_-v15ip-Wl1z1FQ4o4

長い文字列が並んでいますが、内容としてはシンプルで、ドットで区切られた3つの部分に分かれています。

  1. ヘッダー
  2. ペイロード
  3. 署名

つまりヘッダー.ペイロード.署名の形式になっているということです。

ヘッダー・ペイロードはBase64UrlエンコードされたJSONオブジェクトで、署名はその内容を元に暗号学的ハッシュ関数や公開鍵暗号を使って作成されたものになります。

Base64Urlエンコードなので、ヘッダー・ペイロードはデコードすることが可能です。 なので、ヘッダーやペイロードに個人情報や機密情報を含める場合は注意が必要です。(後述するJWEの利用で技術的に対応は可能です)

例えば、上記のJWTをデコードすると以下のようなJSONオブジェクトが得られます。

ヘッダー

{
  "alg": "HS256",
  "typ": "JWT"
}

algは必須で、署名のアルゴリズムを指定します。 HS256(共通鍵)、RS256(公開鍵)などがあります。 HS256は共通の秘密鍵を使って署名を行うアルゴリズムで、単一のサーバーで完結するWebアプリケーションなどでよく使われます。 RS256は公開鍵と秘密鍵のペアを使って署名を行うアルゴリズムで、複数のサーバー間で署名の検証を行う場合などに適しています。(マイクロサービス構成などでよく使われます)

typは任意で、トークンの種類を指定します。通常は”JWT”が設定されます。 “JWE”は暗号化されたJWTを表します。どうしてもトークンの内容を第三者に知られたくない場合や厳格なセキュリティ要件がある場合に使用されます。

ペイロード

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": false,
  "iat": 1516239022
}

RFC 7519で定義された規格があります。参照: RFC 7519

上記の例でいうと”sub”,“iat”がRFC 7519で定義された登録済みクレームになり、“name”や”admin”は独自に定義されたクレームになります。

署名

署名はBase64Urlエンコードされたヘッダー.ペイロードを秘密鍵でハッシュ化したものです。これにより、JWTの内容が改ざんされていないことを検証することができます。

例えば、先ほどのペイロードのadminfalseからtrueに変更すると、ヘッダー.ペイロードまではBase64Urlエンコードで生成が可能ですが、署名は秘密鍵を使って生成されるため、改ざんが検知されるということです。

便利なツール

JWTのデバッグには、jwt.io(Auth0/Okta)が便利です。ここでは、JWTのヘッダー、ペイロード、署名をそれぞれ確認したり、署名の検証を行ったりすることができます。

シーケンス図

ここまでは文字での説明ばかりになってしまったため、シーケンス図でJWTのやり取りの流れを視覚的に示します。

sequenceDiagram
    participant User
    participant Client
    participant Server
    participant Database

    User->>Client: ログイン情報の入力
    Client->>Server: ログイン情報の送信
    Server->>Database: ユーザー情報の照会
    Database-->>Server: ユーザー情報の返却
    Server-->>Client: JWT発行
    Client->>Server: JWTを提示(アクセス要求)
    Server-->>Client: JWT検証後、アクセス許可
    Client->>User: アクセス結果の表示
    User->>Client: 再度アクセス要求(JWT提示)
    Client->>Server: JWTを提示(アクセス要求)
    Server-->>Client: JWT検証後、アクセス許可
    Client->>User: アクセス結果の表示

JWTのメリット

この流れを踏まえると、JWTには以下のようなメリットがあります。

  • サーバー側でセッション管理を行う必要がなくなる セッション方式だと、サーバー側に「誰がログイン中か」のような情報を保存しておく必要がありました。JWTであればトークン自体にユーザー情報や権限情報を含めることができるので、サーバー側でのセッション管理が不要になります。
  • データベースへのアクセス回数が減るため、負荷が軽減される 初めにログイン→JWT発行でデータベースアクセスを行うのみで、以降のアクセスでは認証・認可はクライアントとサーバー間でのJWTのやり取りだけで完結します。
  • サーバーの拡張性が向上する Redisなどのセッションストアに依存する必要がないため、サーバーの水平スケーリングが容易になる。

JWTのデメリット

メリットだけではなく、いくつかのデメリットもあります。

  • 途中で無効化することが難しい JWTは一度発行されると有効期限まで無効化できないため、ユーザーのログアウトや権限変更の反映を即時行いたいときに困ることがあります。ブラックリスト方式で対応も可能ですが、DBやRedisなどのストアを用意する必要があり、メリットをなくしてしまうことがあります。
  • トークンサイズの増加による通信コストの増大 セッションIDと比較すると、JWTはペイロードを含むためサイズが大きくなりがちで、HTTPヘッダーに含めて送信する際の通信コストが増加します。
  • クライアント側でJWTを保持するため、適切な保護がされていないと盗まれるリスクがある。 例えば、ブラウザのローカルストレージに保存している場合、XSS攻撃によってトークンが盗まれる可能性があります。セキュリティを確保するためには、保存場所や送信方法に注意が必要です。

まとめ

JWTはサーバー側でのセッション管理を不要にし、データベースアクセスの負荷を軽減し、サーバーの拡張性を向上させるなどのメリットがあります。その一方で、途中で無効化することが難しい、トークンサイズが大きくなる、クライアント側での保護が必要などのデメリットも存在します。

JWTを利用する際は、これらのメリットとデメリットを理解した上で、システム要件にあった設計を選ぶのが大事になります。