JWT (JSON Web Token)が、
データを JSON 形式で保持出来て、
内容の改ざんを検知することができるトークンであることはよく知られているかなと思います。
認証周りの機能で用いられていたり、ユーザーの情報などを保持するために利用されていることがあるので使う機会は多々ありますよね。
ただ JWT が実際にどのような仕組みで出来ているのかまではあまり気にしたこともない人もいるのかなと思います。今回はどのような仕組みで構成されているのかを少し見ていきましょう。
特徴
まず、特徴から見ていきましょう。
ざっと以下が挙げられる様です。
- 改ざんの検証をすることができる
- 暗号化前のデータが JSON 形式のオブジェクトである
- URL Safe である (GETパラメータのセパレータとして機能する ? = & が含まれない仕様)
- コンパクトにやり取りを行うことができる
- 署名に公開鍵と秘密鍵のペアを使用できる(SWTは共有鍵のみ)
先ほども出た改ざんの検証が出来ることや JSON 形式のオブジェクトであることに加えて、URL Safe であることやコンパクトであることも特徴であるみたいですね。
こういった特徴を持つ理由を仕組みから探してみます。
仕組み
JWT は、Header (ヘッダ)・Payload (ペイロード)・Signature (署名) と大きく 3 つの要素からなる文字列となっています。各要素は、ドット(.)で結合されて 1 つの文字列として扱われます。
// ヘッダ.ペイロード.署名 から成る 1 つの文字列になっている
abaA1.BBaa2.ABBAa3
次は各要素の詳細を見ていきましょう
以下の画像はjwt.ioから引用したものです。
先述した JWT の 3 つの構成にリアルな値が入ったサンプルになっています。
各要素の中に定義されている項目のことは クレーム (Claim) と呼びます。
クレームには必須のものや任意のもの、独自に定義できるものがあり、各クレームに保持させたい情報を入れて扱う形となっています。
アプリケーションからユーザーの情報などを JWT に持たせることがあると思うのですが、そういった重要な情報は主にペイロードの部分に格納されています。
次に各要素の中身を見ていきましょう
ヘッダ(HEADER)
ヘッダは、主にメタ情報となるものが含まれる場所になっています。具体的には以下のキーを含めることが可能です。
-
alg
:署名の際に使用されるアルゴリズム -
typ
:トークンのタイプ -
cty
:ペイロードのメディアタイプ -
kid
:署名に使う鍵の ID
上記の画像だと、HS256
というアルゴリズムで署名を行い、 JWT
というトークンのタイプであることがヘッダから分かりますね。
最終的には base64UrlEncode されます。
ペイロード(PAYLOAD)
ペイロードは、実際にアプリケーションで活用される情報が含まれます。具体的には以下のキーを含めることが可能です。
-
iss
: JWTを発行した者(サーバー)の識別子 -
sub
: JWTの主体となる識別子 -
aud
: JWTを利用する側(クライアント)の識別子 -
exp
: 有効期限の終了日時 -
nbf
: 有効期限の開始日時 -
iat
: 発行日時 -
jti
: 一意な識別子 -
typ
: コンテンツタイプの宣言
この他にも id だったりメールアドレスだったり持たせたければキーバリューの形で持たせることが出来ます。
こちらも最終的には base64UrlEncode されて文字列の一部となります。
署名(SIGNATURE)
署名は、JWT の改ざんを検証する際に使用される文字列になります。
署名の文字列は、JWT のヘッダに付与されているアルゴリズムとシークレットキー(署名の際に使用される鍵のこと。今回の画像では共通鍵を利用しているがその他にも公開鍵・秘密鍵を用いるやり方もある)を利用して署名のための文字列を生成します。
この部分が特徴にも挙がっていた 改ざんの検証をすることができる
というところですね。
ヘッダとペイロードを base64UrlEncode した上でドットで繋げて、HMACSHA256 のアルゴリズムにシークレットキーと共に渡すことで署名をしている様です。
そうして作られたハッシュ値を他の要素と同様に base64UrlEncode して、文字列の一部とします。
検証ってどの様に行われるのか
JWT が大体どういう構成から成っているのかというところは概ね知ることが出来たと思います。
最後に、特徴の一つであった 改ざんの検証をすることができる
ところはどの様に行われるのかというところを見ていきましょう。
大まかには以下のような流れになります。
まず、ヘッダ・ペイロードに関しては base64UrlEncode されているだけなので、decode を行います。
そして最初に kid が一致するのかなどの確認を行う様です。ヘッダに関しては暗号化されているわけではないので改ざんすることも可能です。なので大事なのは次の署名の検証になります。
JWT を構成する 3 つの部分の内の 1 つである署名部分は、シークレットキーを用いてハッシュ化されています。ハッシュ値は不可逆なので復号化をすることは出来ません。
なので、送り元と同様にヘッダー・ペイロードをエンコードした上でシークレットキーを用いてハッシュ値を再生成します。そしてそれを送られてきたハッシュ値と突き合わせて一致していれば改ざんされていない、という検証を行う様です。
おわり
以上になります。
なんとなくでも理解の一助になれば幸いです。