まえがき
- もともと自分の理解を目的にしてまとめてみましたが、少しでもお役に立てたら嬉しいです。
- 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": "testsnap@timers-inc.com" // カスタム
"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の概要についてまとめてみました。少しでも役に立てれば嬉しいですし、何か間違っていることなどあればコメントいただきたいです。
- 最後までお読みいただき、ありがとうございました!