JWT(JSON WEB TOKEN)入門
JWTは「JSON Web Token」の略です。JWTは今一番流行っているクロスドメイン認証ソリューションです。
1. クロスドメイン認証問題
ユーザ認証は一般的に下記の流れです。
① クライアントはサーバーへユーザ名、パスワードを送信する。
② サーバー側は認証を通りますと、セッション(session)に認証データ(ロール、ログイン時刻など)を保存する。
③ サーバーはクライアントへsession_idを返して、クライアントのCookieに書き込む。
④ クライアントはリクエストごとにCookieに保存されているsession_idをサーバーに渡す。
⑤ サーバー側はsession_idによって保存されたデータからユーザの権限を取得する。
このやり方は下記の課題があります。
- サーバー性能問題
ユーザ認証後、ユーザ情報を作成してサーバー上に保存します。通常はメモリに保存されますので、ユーザは多くなりますと、性能が落ちる可能性があります。 - スケーラビリティ
サーバー負荷によって台数を増やしたり減らしたりすることによって、セッションの維持、共有管理をしないといけないですので、DBなど共有サーバーで管理する必要です。 - クロスドメイン対応
シングルサイトの場合、特に問題がないですが、複数サイトをまたがる場合、セッション情報の共有が必要となりあす。例えば、AサイトとBサイトは某会社傘下のサービスです。ユーザはサイトAにログインして、サイトBも自動ログインする要件に対して、セッション情報をデータベースに書き込んで、リクエストを受けると、データベースからセッションを取得する必要です。
JWTを使って、サーバー側はセッションデータを保存せず、すべてのデータをクライアント側に保存する。リクエストごとにその情報をサーバーへ送信しますと、上記の課題は解決できます
2. JWTの原理
JWTはサーバー認証後、JSONオブジェクトを生成して、クライアント側に返します。
{ "name": "takahasi", "role": "admin", "expiration": "2019-9-10 00:00:00" }
その後、クライアントはサーバーへリクエストを送るたびに、このJSONオブジェクトをつけます。サーバー側はこの情報を使ってユーザ認証を行います。データ改ざんを防ぐために、サーバーはこのオブジェクトを生成するときに、署名もつけます。
サーバー側はセッションを保存しないと、ステートレスになりますので、スケーラビリティは簡単にできるようになります。
3. JWTのデータ構造
実際のJWTは下記のようです。
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0IiwiZXhwIjoxNTY3OTAxNDcyLCJpYXQiOjE1Njc5MDE0NTR9.sey05o9p_SniMc7mYKs63lRRz6KhELQ1qeDMUY-OL58wUqGDnRaGf8nQIW2v3YvTmOHN-8UqA-zP1rulVLBizA
長い文字列で、ピリオド「.」で3段を区切ります。3つの部分は下記の通りです。
Header Payload Signature
一行で書くと、
Hedaer.Payload.Signature
公式サイト(https://jwt.io/)を見れば、わかりやすいです。
3.1 Heaer
Header部分はJSONオブジェクトです。
{ "alg": "HS256", "typ": "JWT" }
alg属性は署名のアルゴリズムです。デフォルトは「HMAC SHA256」(HS256)です。
typ属性はトークンの種別です。
Header部分をBase64URLアルゴリズムで文字列に変換します。
3.2 Payload
PayloadもJSONオブジェクトです。
実際の送信データを保存します。7つの標準項目が定義されています。
No. | key | detail | comment |
---|---|---|---|
1 | iss | issuer | 発行者 |
2 | exp | expiration time | 失効日時 |
3 | sub | subject | サブジェクト |
4 | aud | audience | 想定利用者 |
5 | nbf | Not Before | 有効日時 |
6 | iat | Issued At | 発行日時 |
7 | jti | JWT ID | ユニックID |
注意:JWTデフォルトは暗号化されないため、誰でも読めますので、秘密情報はこの部分に入れないでください。
Payload部分もBase64URLで文字列に変換します。
3.3 Signature
Signature部分は前の2つの部分の署名となり、データ改ざんを防ぎます。
まず、秘密キーを指定して、このキーはサーバー側しかなく、ユーザに教えないこと。
次は、ヘッダに指定した署名アルゴリズム(デフォルトHMAC SHA256)で下記の算式で署名を生成すします。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
署名を生成してから、Header、Payload、Signature3つの部分を「.」で区切って1つの文字列に結合してクライアント側に返します。
3.4 Base64URL
HeaderとPayloadの文字列変換はBase64URLとういアルゴリズムを使います。
このアルゴリズムはBase64アルゴリズムと似ていますが、若干違います。
JWTはトークンとして、URLに入れる可能性もあります。
例)https://api.example.com/?token=xxx
Base64の「+」「/」「=」はURLには特殊な意味があるため、Base64URLでは「=」を省略して、「+」を「-」に変換、「/」を「_」に変換します。
4. JWTの使い方
基本的な使い方は下記のイメージです。
①クライアントは認証リクエストを投げる。
②認証サーバーからトークンを返す。
③クライアントはそのトークンを利用してリソースサーバーをリクエストする。
クライアントはサーバーから返却したJWTはCookieに保存できますし、localStorageに保存できます。
クライアントからサーバーへのリクエストは必ずJWTをつけます。Cookieに入れて自動送信でも可能ですが、ドメインを跨がれないですので、一般的なやり方はHTTPリクエストのヘッダに「Authorization」に設定します。または、POSTリクエストのボディに入れても可能です。
Authorization: Bearer <token>
5. JWTの特徴
- JWTは暗号化しない場合、秘密情報をJWTに書かないことです。
- JWTは認証以外、情報交換でも使えます。JWTを有効に利用すれば、サーバー側DBの検索回数を下げることが可能です。
- JWTはセッションを保存しないため、利用中token廃止、token権限変更などはできない。つまり、tokenを発行しますと、ロジックを追加しない限り、失効までずっと有効になります。
- JWTには認証情報が入っていますので、漏洩したら、誰でもこのトークンの権限を取得できます。なりすましを防ぐために、JWTの有効期間を短くに設定したほうがよいと考えられます。重要な権限であれば、使用時に再度認証のほうが良いでしょう。
- JWTはHTTPではなく、HTTPSを使うべきです。