JWT(+JWS)について調べた
JWT とは
JSONオブジェクトで、安全に2者間で情報をやりとりするための表現方法です。 RFC7519で定義されています。
JWS とは
JSON Web Signatureの略、JWTのエンコード形式、デジタル署名、もしくはMACを行ったメッセージの表現 RFC7515で定義されています。 他には、暗号化するための仕様としてJSON Web Encryption (JWE)なんかがあります。
今回は、使われているケースが多いJWT+JWSの組み合わせのパターンでの実装を調べていきたいと思います。
特徴
- コンパクトである
- サイズが小さいので、URL、POSTパラメータ、またはHTTPヘッダー内で送信できます。
- 自己完結されている
- url-safeである
- URLで使用できる文字列のみで構成されています。
どんなときに使うのか
認証
- こちらが一般的な使われ方のようです。
- ユーザーがサービスにログインしJWTを発行してらもらい、その発行されたJWTで他に許可された異なるドメインのリソースを取得する。などの使い方ができます。
情報の交換
OpenID ConnectやOAuthと組み合わせて使うケースもあるようです。
JWTの構造
- ヘッダ
- ペイロード
- 署名
これらをBase64エンコーディング(urlsafe、パディング無し)に変換し、.
区切りで結合します。
こんな形になります。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MTU0MDY1MjQwMH0.a2xzydhHuo2UREDRIdcyKmlihl_1gl_BYCxdQEKmgTE
ヘッダ
ヘッダには、このトークンのタイプ、使用されているハッシュアルゴリズムなどの情報で構成されます。
この例では、
ハッシュアルゴリズムが、HS256
(HMAC SHA-256アルゴリズム)
トークンのタイプが、JWT
ということを示しています。
{ "alg": "HS256", "typ": "JWT" }
ペイロード
ペイロードは以下のような形式になります。 ペイロードにはクレームを含んでいます。 (クレームとは、エンティティ(通常はユーザー)と追加のメタデータのことを指します。)
クレームには、以下3種類があります。
予約済み
公開済み
個人用
- これらは、それらの使用に同意する当事者間で情報を共有するために作成されたカスタムクレームです。
{ "sub": "1234567890", "name": "John Doe", "admin": true, "exp": 1540652400 }
有効期限(exp)は、 NumericDate
である必要があるので、ここでは、UNIXTIMEを入れています。
https://tools.ietf.org/html/rfc7519#section-4.1.4
署名
先ほど、紹介したJOSE ヘッダ、JWSペイロード、(Base64urlエンコード済)を、
HMAC SHA-256
アルゴリズムで署名し,
[JWS]に指定された形で署名をbase64urlエンコードすると, このエンコード済JWS署名を作り出せます。
RubyでJWTを作ってみる
# -*- coding: utf-8 -*- require 'base64' require 'json' require 'openssl' header = { alg: 'HS256', typ: 'JWT' } payload = { sub: '1234567890', name: 'John Doe', admin: true, exp: 1540652400 } secret = 'secret' encoded_header = Base64.urlsafe_encode64(header.to_json).delete('=') encoded_payload = Base64.urlsafe_encode64(payload.to_json).delete('=') signature = OpenSSL::HMAC.digest( OpenSSL::Digest::SHA256.new, secret, "#{encoded_header}.#{encoded_payload}" ) encoded_signature = Base64.urlsafe_encode64(signature).delete('=') jwt = [ encoded_header, encoded_payload, encoded_signature, ].join('.') puts '[jwt] => ' + jwt
結果
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MTU0MDY1MjQwMH0.a2xzydhHuo2UREDRIdcyKmlihl_1gl_BYCxdQEKmgTE
値の確認
こちらのjwt.ioというサイトのDebuggerツールで、値の確認ができます。
JWTの検証
こちらは、別の記事でまとめていく予定です。