JSON Web Tokenによる認証

こんにちは。サイオステクノロジー技術部の武井です。

今回は、JSON Web Tokenの仕様に従ったアクセストークンをLinuxのコマンドラインで作成してみたいと思います。

その前に、JSON Web Tokenを説明したいと思います。

JSON Web Tokenとは?

JSON Web Tokenとは、JSONに電子署名を行い、必要に応じてそのJSONの検証を行い、認証可否を決定する仕様です。OpenIDやOAuthなどに用いられている標準的な仕様です。

セッションを使うことでも同様のことを実現ができますが、JSON Web Tokenの場合は、トークンそのものを検証することで認証可否を判断するので、セッションとは違い、サーバー側で何も持つ必要がありません。これにより、トークンを発行したホストと、トークンを検証するホストが別々でもよいのです。

よくある説明なのですが、イマイチわかりにくいと思います。

JSON Web Tokenの仕様に従って作成されたトークンは以下のようになっています。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e-KAnHN1YuKAnTrigJxudGFrZWnigJ19.97763576b44cbadea825195aa610b3b9ce71a52f228f3a2b41eba30e7e8350ad

長い文字列ですが、構成としては、ヘッダー、ペイロード、ヘッダーとペイロードをピリオドでつないで署名したものの3つをピリオドでつなげたものです。つまり以下のような感じです。

[ヘッダー].[ペイロード].[ヘッダーとペイロードをピリオドでつないで署名したもの]

ヘッダー、ペイロードをそれぞれ説明します。

ヘッダー

署名アルゴリズムとトークンのタイプを定義します。具体的には以下のようなJSONになります。

{ 
    “alg”: “HS256”, 
    “typ”: “JWT”
}

これは署名アルゴリズムはHS256、トークのタイプがJSON Web Tokenであることを表しています。トークンのタイプは他にもJWE(JSON Web Encryption)と言うものがあり、これは暗号化まで仕様として定義しているようです。しかしながら、今回は暗号化は行わず、あくまで署名だけですので、SSL等で暗号化することは必須となります。

ペイロード

JSON Web Tokenでやり取りするデータ本体です。例えばユーザー名だったり、認可情報だったり、ユーザーが自由に定義します。例えば以下のような感じです。

{ 
    “sub”: “ntakei”
}

これは、ユーザー名がntakeiのデータになります。ただ、何でも定義していいというわけではなく、幾つかのフィールド名はJSON Web Tokenの仕様によって予約されています。詳細はRFCを参照頂ければわかるのですが、例えば、expというフィールド名はトークンの有効期限として使われることが決まっているので、使ってはいけません。

次に、トークンを作成するまでの流れ、そしてそのトークンを検証するまでの流れを説明したいと思います。

トークン作成の流れ

トークン作成までの流れは以下のようになります。

Slide1

(1) ヘッダーをBase64 URL Encode

ヘッダーをBase64 URL Encodeします。Base64 URL EncodeとはBase64でエンコードした上で、更に以下の処理を施します。

 

  • +を-に置換
  • /を_に置換

これはURLに、+や/を乗せるとおかしな動作をする可能性があるからです。しかし、個人的には、トークンをURLに付与することがあるのかなと思っています。大抵は、HTTPリクエストヘッダのAuthorizationにセットするのが普通なのではないでしょうか?でもJSON Web Tokenの仕様なので、そのとおりにする必要があります。

(2) ペイロードをBase64 URL Encode

ヘッダーと同様にペイロードもBase64 URL Encodeします。

(3) HS256アルゴリズムとシークレットキーにより署名

(1)と(2)の値をピリオドでつなげて、その値のハッシュ値(不可逆で、その値からはもとの値が類推できないもの)を求めます。ハッシュ値は、HS256アルゴリズムと、サーバー側に保存されたシークレットキーで求めます。このハッシュ値を求めることを署名と言います。

そして、その(1)と(2)と(3)をそれぞれピリオドでつなげたものが、最終的にクライアントに渡されるトークンになります。

このトークンを受け取ったクライアントは、メモリなりデータベースなりローカルストレージなり永続化されるストレージに保存をして、認証で保護されたリソースにアクセスしたときは、ストレージから取得して、サーバーに渡します。

トークンを検証するまでの流れ

クライアントからトークンを受け取ったサーバーが、どうやって認証OKと判断するかを説明します。

Slide2

(1) HS256アルゴリズムとシークレットキーにより署名
クライアントから受け取ったトークンをピリオドで区切って、左から一つ目と二つ目(つまりヘッダーとペイロード)のハッシュ値を求めます。求め方は、先ほどと同じです。

(2) 一致しているかどうか比較
(1)で求めた値と、トークンをピリオドで区切ったものの中の一番右の部分(つまり、「トークン作成の流れ」でヘッダーとペイロードを署名したもの)が一致しているかどうかを比較します。これが一致していない場合は、認証情報が偽造もしくは改ざんされたものと判断し、認証NGとします。

アクセストークンを自分でつくってみる

Linuxのコマンドを使って、JSON Web Tokenの仕様に基づいたトークンを作って見たいと思います。

下記のようなJSONがあるとします。これらからトークンを作ります。

ヘッダー

{ 
    “alg”: “HS256”, 
    “typ”: “JWT”
}

ペイロード

{ 
    “sub”: “ntakei”
}

まずヘッダーをBase64 URL Encodeします。

# echo -n '{"alg":"HS256","typ":"JWT"}' | base64 | sed s/\+/-/ | sed -e 's/\//_/' | sed -E s/=+$//
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

まずbase64コマンドでbase64エンコードして、sedで+を-に置換、さらにsedで/を_に置換、さらにsedでBase64の最後に付加される=のパディング文字列を削除しています。

同様に、ペイロードもBase64 URL Encodeします。

# echo -n '{“sub”:“ntakei”}' | base64 | sed s/\+/-/ | sed -e 's/\//_/' | sed -E s/=+$//
e-KAnHN1YuKAnTrigJxudGFrZWnigJ19

次にヘッダーとペイロードをピリオドでつなげたものを、HS256アルゴリズムとシークレットキー(ここではsecret)でハッシュ値を求めます。

# echo -n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e-KAnHN1YuKAnTrigJxudGFrZWnigJ19'  | openssl dgst -sha256 -hmac secret
97763576b44cbadea825195aa610b3b9ce71a52f228f3a2b41eba30e7e8350ad

今までの過程で得られた3つの値をピリオドでつなげます。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e-KAnHN1YuKAnTrigJxudGFrZWnigJ19.97763576b44cbadea825195aa610b3b9ce71a52f228f3a2b41eba30e7e8350ad

これがJSON Web Tokenの仕様に従ったトークンになります。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

10人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です