主要な認証サービスで利用されているJWT(JSON Web Token)の概要を理解する


まえがき

  • もともと自分の理解を目的にしてまとめてみましたが、少しでもお役に立てたら嬉しいです。
  • JWTの概要について書いており、活用例などは書いてありません。
  • JWTをお聞きしたことのない方(僕もそうでした)でも、 主要な認証サービスで使われておりいずれ遭遇すると思うので、今のうちに頭の片隅に入れておいて損はないと思っています。

JWT のサンプル

まずイメージを持っていただくために、JWTのサンプルを用意しました。

JWTのサンプル
eyJraWQiOiJZRnJwVUpsSzZqQlErTC9VcGpVbTgZFZQa3lOaWZmOgBHVnJvgWNBYjFcL3c9IiwiYWTnIjoiUlMyNTYifQ.eyJzgWIiOiI2ZjghMGFiMi03MjQwLTQyZmEtYTRlYS04ZgVkYzY0OgU2NTUiLCJhgWQiOiI2MnZsgHQzMnE2gjlwczE4ZHE3bTFsZ2w5aCIsImVtYWlsT3ZlcmlmaWVkIjp0cnVlLCJlgmVugF9pZCI6IjY3MzMwZjMTLTcTZmItMTFlOS04ZTZhLTRkNjBlMmZmNmNkMyIsInRva2VuT3VzZSI6ImlkIiwiYTV0aF90aW3lIjoTNTU3MzY2MgE5LCJpc3MiOiJogHRwczpcL3wvY29nbml0by3pZHAuYTAtbm9ygGhlYTN0LTEuYW3hem9uYTgzLmNvbVwvYTAtbm9ygGhlYTN0LTFfajRFZ0JMTmRTIiwiY29nbml0bzp3c2VybmFtZSI6IjZmN2EwYWIyLTcyNgAtNgJmYS3hNGVhLThkNWRjNjQ4NTY3NSIsImV4cCI6MTU3NzM2OTYTOSwiaWF0IjoTNTU3MzY2MgE5LCJlbWFpbCI6InRlc3RzbmFwQHRpbWVycy3pbmMuY29tIn0.GR4Uo2EjWJwgtk7jFLPRB003A_bl0TlR-OoMLLlnB9KZBT93KCgbOQrhMV_4o0oHwjBm2p3zfCy0ZAzf7gIc_VbFKhTiK43pF2Uu850pPgepnnWmkUgWrpSVcnHFUs6SKTZUZsllJGQqr_gsoiYHgjYEkTgl60Mg3LUM7J4_vErB_7spi7yltgMumGaKiuFoL338My7YJOU3-BI4NBjGR86VQn4vcaHECOPSheTGmEW5B7jeTgk0MSAN0ykAHt6Sq7ng4Tg3nLEUcpeTA3Nt3Rw8S08glq_5ZGcyoJgAlsP7gCVg3kRNN9uJ_0eimkip7R6f02gsf_Ok8pT-IW4kag

これをみてもさっぱりだと思いますが、ここからの説明を眺めていただき、概要を理解していただけると幸いです。

JWT の概要

  • JWTとは、JSON Web Token の略であり、JWT(ジョット)と呼ばれます。

  • RFC 7519で標準化された仕様であり、様々な認証サービスで利用いられています。

    • OAuth2、OpenID Connect、AWS Cognito...
    • (補足) RFC: インターネット技術の標準化などを行うIETF(Internet Engineering Task Force)が発行している、技術仕様などについての文書群
  • JWT内に任意の情報(=クレームと呼びます)を保持することができます。

    • Username, Email etc...
  • 秘密鍵/公開鍵や、共通鍵を用いた電子署名により、JSONの改ざんをチェックできるようになっています。

    • 暗号化のアルゴリズム(RS256, HS256 etc...)によって、鍵の仕組みが変わっています。
    • トークンの発行者や受け取った方が、鍵を用いて JSON が改ざんされていないこと、トークンが正しいことを検証出来ます。

JWTの利用ケース例

JWTの構成概要

  • 記事の最初に書かれているJWTサンプルの再掲です。
JWTのサンプル
eyJraWQiOiJZRnJwVUpsSzZqQlErTC9VcGpVbTgZFZQa3lOaWZmOgBHVnJvgWNBYjFcL3c9IiwiYWTnIjoiUlMyNTYifQ.eyJzgWIiOiI2ZjghMGFiMi03MjQwLTQyZmEtYTRlYS04ZgVkYzY0OgU2NTUiLCJhgWQiOiI2MnZsgHQzMnE2gjlwczE4ZHE3bTFsZ2w5aCIsImVtYWlsT3ZlcmlmaWVkIjp0cnVlLCJlgmVugF9pZCI6IjY3MzMwZjMTLTcTZmItMTFlOS04ZTZhLTRkNjBlMmZmNmNkMyIsInRva2VuT3VzZSI6ImlkIiwiYTV0aF90aW3lIjoTNTU3MzY2MgE5LCJpc3MiOiJogHRwczpcL3wvY29nbml0by3pZHAuYTAtbm9ygGhlYTN0LTEuYW3hem9uYTgzLmNvbVwvYTAtbm9ygGhlYTN0LTFfajRFZ0JMTmRTIiwiY29nbml0bzp3c2VybmFtZSI6IjZmN2EwYWIyLTcyNgAtNgJmYS3hNGVhLThkNWRjNjQ4NTY3NSIsImV4cCI6MTU3NzM2OTYTOSwiaWF0IjoTNTU3MzY2MgE5LCJlbWFpbCI6InRlc3RzbmFwQHRpbWVycy3pbmMuY29tIn0.GR4Uo2EjWJwgtk7jFLPRB003A_bl0TlR-OoMLLlnB9KZBT93KCgbOQrhMV_4o0oHwjBm2p3zfCy0ZAzf7gIc_VbFKhTiK43pF2Uu850pPgepnnWmkUgWrpSVcnHFUs6SKTZUZsllJGQqr_gsoiYHgjYEkTgl60Mg3LUM7J4_vErB_7spi7yltgMumGaKiuFoL338My7YJOU3-BI4NBjGR86VQn4vcaHECOPSheTGmEW5B7jeTgk0MSAN0ykAHt6Sq7ng4Tg3nLEUcpeTA3Nt3Rw8S08glq_5ZGcyoJgAlsP7gCVg3kRNN9uJ_0eimkip7R6f02gsf_Ok8pT-IW4kag
  • JWTの中には、ピリオド(.) が2つ含まれており、それを境目にして3つの文字列に分割できます。
    • 上記のJWTを実際に分割してみると、以下のようになります。
JWTのサンプルをピリオドで分割する

eyJraWQiOiJZRnJwVUpsSzZqQlErTC9VcGpVbTgZFZQa3lOaWZmOgBHVnJvgWNBYjFcL3c9IiwiYWTnIjoiUlMyNTYifQ
.
eyJzgWIiOiI2ZjghMGFiMi03MjQwLTQyZmEtYTRlYS04ZgVkYzY0OgU2NTUiLCJhgWQiOiI2MnZsgHQzMnE2gjlwczE4ZHE3bTFsZ2w5aCIsImVtYWlsT3ZlcmlmaWVkIjp0cnVlLCJlgmVugF9pZCI6IjY3MzMwZjMTLTcTZmItMTFlOS04ZTZhLTRkNjBlMmZmNmNkMyIsInRva2VuT3VzZSI6ImlkIiwiYTV0aF90aW3lIjoTNTU3MzY2MgE5LCJpc3MiOiJogHRwczpcL3wvY29nbml0by3pZHAuYTAtbm9ygGhlYTN0LTEuYW3hem9uYTgzLmNvbVwvYTAtbm9ygGhlYTN0LTFfajRFZ0JMTmRTIiwiY29nbml0bzp3c2VybmFtZSI6IjZmN2EwYWIyLTcyNgAtNgJmYS3hNGVhLThkNWRjNjQ4NTY3NSIsImV4cCI6MTU3NzM2OTYTOSwiaWF0IjoTNTU3MzY2MgE5LCJlbWFpbCI6InRlc3RzbmFwQHRpbWVycy3pbmMuY29tIn0
.
GR4Uo2EjWJwgtk7jFLPRB003A_bl0TlR-OoMLLlnB9KZBT93KCgbOQrhMV_4o0oHwjBm2p3zfCy0ZAzf7gIc_VbFKhTiK43pF2Uu850pPgepnnWmkUgWrpSVcnHFUs6SKTZUZsllJGQqr_gsoiYHgjYEkTgl60Mg3LUM7J4_vErB_7spi7yltgMumGaKiuFoL338My7YJOU3-BI4NBjGR86VQn4vcaHECOPSheTGmEW5B7jeTgk0MSAN0ykAHt6Sq7ng4Tg3nLEUcpeTA3Nt3Rw8S08glq_5ZGcyoJgAlsP7gCVg3kRNN9uJ_0eimkip7R6f02gsf_Ok8pT-IW4kag
  • . で分けられたtokenはそれぞれに意味があり、JWTは3つの要素で構成されています。
構成要素 要素の概要
ヘッダー 署名生成に使用したアルゴリズムや、トークンそのものに関する情報が格納している
ペイロード クライアントとのやりとりに必要なクレームを格納している
電子署名 (ヘッダー+ペイロード+ 秘密鍵) を、アルゴリズムで暗号化した文字列
{Base64urlエンコードされたヘッダー}.{Base64urlエンコードされた ペイロード}.{電子署名}
  • ※ JWTはURIのクエリパラメーターなどに使用されることを想定しているので、URL-safeに表現するためにBase64urlエンコードした文字列となっています。
    • Base64エンコードの場合は +, /, = が含まれてしまい、URLパラメータとしては不適切になってしまいます。

JWTの構成要素詳細

1. ヘッダー

概要

  • 署名生成に使用したアルゴリズムや、トークンそのものに関する情報が格納されています。
  • キー名と値のペアで表現されたJSONをBase64urlエンコードした文字列となっています。
JWTヘッダーをデコードした値例
{
  "typ": "JWT",
  "alg": "HS256"
}

keyの種類

key 意味
typ tokenの形式
alg 署名アルゴリズム (HS256: HMAC SHA-256)

2. ペイロード

概要

  • クライアント側とのやり取りで必要となる属性情報(=クレーム)が格納されています。
  • データはアプリケーション任意のものなので、必須となるものは存在しません
  • RFCにおいては、ペイロードに含める以下のような標準クレームが定義されています。(予約済みクレームといいます)
JWTペイロードをデコードした値例
{
  "sub": "6f7a0ab2-7240-42fa-a4ea-xxxxxxxx",
  "aud": "62vltt32xxxxxxxx",
  "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/xxxxxxxxxx",
  "exp": 1557369619,
  "iat": 1557366019,
  "auth_time": 1557366019,
  "event_id": "67330f31-71fb-11e9-8e6a-xxxxxxxx",   // カスタム,
  "token_use": "id",   // カスタム,
  "cognito:username": "6f7a0ab2-7240-42fa-a4ea-xxxxxxxxxx"  // カスタム,
  "email": "[email protected]" // カスタム
  "email_verified": true,  // カスタム
  "phone_number": "090-1234-5678" // カスタム
}

keyの種類

予約済みクレーム 名称 説明
iss Issuer トークン発行者の識別子。一般的にアプリケーション固有となります.
sub Subject トークン主体の識別子。一般的にアプリケーション固有となります.
aud Audience トークンが意図している受信者の識別子。トークンを受け付ける受信者は、この値に自身が含まれるかを識別しなければなりません。もしaudクレームが存在し、かつ自身が含まれない場合、トークンを拒否しなければなりません。
exp Expiration Time トークンの有効期限。この期限以降の場合、トークンを受け付けてはなりません。有効期限は1970-01-01 00:00:00Zからの秒数を数値で指定しています(UNIX時間)[10]。
nbf Not Before トークンの開始日時。この期限以降の場合、トークンを受け付けてよい。秒数を数値で指定しています。
iat Issued at トークンの発行日時。秒数を数値で指定しています。
jti JWT ID 発行者ごとトークンごとに一意な識別子。

3. 署名

概要

  • エンコード済みヘッダー + ピリオド(".") + エンコード済みペイロード + 秘密鍵 を、ヘッダー["alg"]の暗号化アルゴリズムで暗号化され生成されます。
  • 秘密鍵を利用しているので、生成者自身が改ざん検証を行うことができます。
HMAC-SHA256形式のコード例
HMAC-SHA256(
 base64urlEncoding(header) + '.' +
 base64urlEncoding(payload),
 秘密鍵
)
  • 秘密鍵でハッシュ化する為、ヘッダーとペイロードを推測できても、署名を第三者が生成することが不可能になります。

JWTのメリット

  • DBへの接続削減
    • サーバーはヘッダーで渡されたトークンが正しいかだけを検証し、アクセスを許可するか判定することができます。
    • 認証に必要な情報は全てトークン内に格納されているので、データベースへの問い合わせを削減することができます。

JWTを利用する上でのセキュリティ注意点

ヘッダ情報algの改ざんによる脆弱性

内容

  • サーバーからJWTを受け取りデコードし、alg:noneに直して再度エンコードすると
    • => algが none なので署名の検証が行われずに、改ざんされたペイロードが利用されてしまいます。

対策

  • algの受け取る値をホワイトリスト形式で制限しましょう。

機密情報のやりとり

内容

  • 機密情報を含めたJWTを送信すると
    • => 悪いクライアントがデコードして情報を盗み取り悪用されてしまいます。

対策

  • クライアント側に見えてはいけない内容はJWTに含めないようにしましょう。

あとがき

  • JWTの概要についてまとめてみました。少しでも役に立てれば嬉しいですし、何か間違っていることなどあればコメントいただきたいです。
  • 最後までお読みいただき、ありがとうございました!