JSON Web Token (JWT) の仕組み
皆さん、こんにちは。技術開発グループのn-ozawanです。
一気に冷え込みましたね。11月を実感しています。
本題です。
JWTはOAuth2.0などの認証認可のトークンでよく使われます。一見すると訳の分からない文字列だったり、デジタル署名だったり、RSA、OAuth2.0、OIDCなどの気難しそうな用語が出てきたりと、JWTって難しそうと思われがちですが、実はそんなことはありません。今日はそんなJWTのお話です。
目次
JSON Web Token (JWT)
概要
JWTは、HTTPヘッダーやクエリパラメータにJSONデータを付与するための仕組み(RFC7519)です。例えばHTTPリクエストでJSONデータを送信するとき、もしくは、HTTPレスポンスでJSONデータを受け取るときに利用します。もちろんPOSTメソッドなどであればBODY部にJSONデータを送信することは出来ますが、GETメソッドなどではBODY部は使えないため十分ではありません。JWTは全てのHTTPメソッドで、HTTPヘッダーやクエリパラメータを利用することでJSONデータを送信することを出来るようにするための規格です。
とはいえ、HTTPヘッダーやクエリパラメータにJSONをそのまま付与することは現実的ではありません。なので、JWTは以下の要件を満たす必要があります。
- JSONデータをURLセーフにする
- JSONデータをコンパクトにする
BASE64URLエンコード
URLセーフとは、URLが正しく認識されることを言います。例えば、URLでは?
、=
、&
などは特別な意味を持ちます。もしJSONデータにそのような記号が含まれている場合、URLは正しく認識されません。そうならないようにJWTではJSONデータをBASE64URLエンコードします。
例えば以下のJSONがあるとします。
{"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}
このJSONをBASE64URLエンコードすると以下のようになります。
eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
余談ですが、BASE64エンコードと、BASE64URLエンコードは別物です。BASE64エンコードではエンコードする際に2種類の文字(+
、/
)を使用しますが、それはURLでは特別な意味を持つ記号となっています。なのでBASE64URLエンコードでは、代わりに-
と_
を使ってエンコードします。
JSONとクレーム
URLやHTTPヘッダーには文字数制限があります。なのでJSONデータをコンパクトにまとめる必要があります。JWTではJSONのデータ項目をクレームと呼び、よく使うクレームの名前を省略形にすることでなるべくコンパクトになるようにしています。
省略形 | 名称 | 説明 |
iss | Issuer | JWTの発行者 |
sub | Subject | JWTの主体の識別子 |
aud | Audience | JWTの受信者 |
exp | Expiration Time | JWTの有効期限 |
nbf | Not Before | JWTの有効開始日時 |
iat | Issued At | JWTの発行日時 |
jti | JWT ID | JWTの一意な識別子 |
JWTの構成
JWTは「ヘッダー」と「ペイロード」と呼ばれるデータ部に分かれています。ヘッダーにはJWTの種類や、この後説明する署名についての情報が記述されています。JWTはこのヘッダーとペイロードをBASE64URLエンコードして、ピリオド(.)で繋ぎます。
JSON Web Signature (JWS)
概要
一見ランダムに見えるJWTでも暗号化している訳ではありませんので、誰でも改ざんが可能です。JWSでは改ざん防止としてJWTに対して署名を行う規格(RFC7515)になっています。署名で用いられるアルゴリズムは主にHS256
、RS256
、ES256
などがあります。
HS256
は、HMAC + SHA-256での署名となります。RS256
は、RSA + SHA-256での署名で、ES256
は、P-256 + SHA-256での署名です。
署名
以下はHS256での署名の例となります。ピリオドで結合されたヘッダーとペイロードを、事前に共有された共通鍵を使ってHMACによる署名を生成し、生成した署名をBASE64URLエンコードします。そして、その結果をヘッダー+ペイロードの後ろに、ピリオドで結合します。
検証
受信側でも同様に、事前に共有された共通鍵にて、ピリオド結合されたヘッダーとペイロードでHMACを行い、署名と同じとなることを確認します。また、そのJWTが有効期限内かどうか、aud
が自分宛となっているかなど、JWTの中身に問題がないか確認して検証を行います。
おわりに
JWTは一見すると訳の分からない文字の羅列が続いているので難しく感じてしまうかもしれませんが、一つ一つを紐解けば大したことはしていません。また、JWTはOAuth2.0やOIDCなどの認証認可と一緒に語られがちですが、認証認可のトークンにJWTが都合良いというだけで、規格としては特に関連はありません。
JWTの中身を簡単に見たい場合はjwt.ioがお勧めです。コピペするだけで、内容の確認から、署名の検証もしてくれます。
ではまた。