JWTの概要と使い所

# JWTとは何か？
## 概要

JSON Web Token（JWTと略され、「ジョット」と呼ばれます）は、URLセーフにJSONオブジェクト（Claim Set）を表現するフォーマットです。
具体的には、Base64URLエンコードしたJSONオブジェクトをJSON Web Signature（JWS） や JSON Web Encryption（JWE）のペイロードとして表現します。
結果的に、JWSやJWEの仕様により、Base64URLエンコードされたものをピリオド（`.`）で連結したものの一部としてそのJSONオブジェクトが表現されます。

https://tools.ietf.org/html/rfc7519#section-1
> JSON Web Token (JWT) is a compact claims representation format
 intended for space constrained environments such as HTTP
 Authorization headers and URI query parameters. JWTs encode claims
 to be transmitted as a JSON [RFC7159] object that is used as the
 payload of a JSON Web Signature (JWS) [JWS] structure or as the
 plaintext of a JSON Web Encryption (JWE) [JWE] structure （omit）.

> The suggested pronunciation of JWT is the same as the English word "jot".

https://tools.ietf.org/html/rfc7519#section-3
> JWTs represent a set of claims as a JSON object that is encoded in a
  JWS and/or JWE structure. This JSON object is the JWT Claims Set.
 
> A JWT is represented as a sequence of URL-safe parts separated by
 period ('.') characters. Each part contains a base64url-encoded
 value. 

JWTで表現されるJSONオブジェクトはClaims Set（Claimの集合）と呼ばれます。ClaimとはJSONのkey, valueの一対を意味します。

https://tools.ietf.org/html/rfc7519#section-2
> JWT Claims Set
  A JSON object that contains the claims conveyed by the JWT.

> Claim
  A piece of information asserted about a subject.  A claim is
  represented as a name/value pair consisting of a Claim Name and a
  Claim Value.

例えば以下のClaim SetをBase64URLエンコードすると以下のようになります。

```json
{"iss":"joe",
 "exp":1300819380,
 "http://example.com/is_root":true}
```

`eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ`

そして（後述する）JWSのCompact Serialization形式のペイロードとして表現すると以下のようになります。

`eyJhbGciOiJub25lIn0`.`eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ`.`dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk`

これ全体は、JWSのCompact Serialization形式でもありJWTでもあります。

## Claims Set

JWTには、三種類のClaimに分類されます。
https://tools.ietf.org/html/rfc7519#section-4

1. Registered Claim Names
IANAの「JSON Web Token Claims」に予め登録されたもので、強制ではないですが推奨されています。

2. Public Claim Names
利用者が自由に名前を決めることができますが、IANAの「JSON Web Token Claims」に登録するか、公的に衝突しない名前である必要があります。

3. Private Claim Names
発行者と利用者間で合意が取れている名前です。

# JWSとは何か？
## JWTとの違い

先ほど、JWTはJSONオブジェクトをURLセーフに表現するフォーマットであることが分かりました。
しかし、それだけだと誰でもBase64URLデコードをして、JSONオブジェクトの中身を見たり、変更したりすることができてしまいます。

それに対処するために、URLセーフでかつ署名を付与して改竄されていないことを保証するフォーマットがJWSで、暗号化を施すのがJWEになります。
JWTはJWSと混同して表現されることが多いですが、JWTの定義からJWSはペイロードがJSONオブジェクト（Claim Set）の場合にJWTでもあることになります。
しかし、JWSはペイロードは任意でJSONオブジェクトに限定していません。したがって、自分の理解が正しければ、JWS may be JWT という関係になります。
イメージとしては、チョコレート菓子（JWT）とケーキ（JWS）のような関係で本質的には別の物ですが、JSONオブジェクトをペイロードとした時はチョコレートケーキになるような理解です。

https://tools.ietf.org/html/rfc7515#section-2
> JWS Payload
  The sequence of octets to be secured -- a.k.a. the message. The
  payload can contain an arbitrary sequence of octets.

https://tools.ietf.org/html/rfc7515#section-3.3
> The UTF-8 representation of the following JSON object is used as the
 JWS Payload.  (Note that the payload can be any content and need not
 be a representation of a JSON object.)
 
ここでは、JWSについて確認していきます。

## 構成

JWSは、JSONオブジェクトに署名を付与したフォーマットであり、JWTを利用することができます。
そして主に以下の三つで構成されます。
https://tools.ietf.org/html/rfc7515#section-3

- JOSE Header
- JWS Payload
- JWS Signature

また、JWSには二つのシリアライゼーション形式があります。

- JWS Compact Serialization
- JWS JSON Serialization

JWS Compact Serialization はヘッダーは署名付きのもの（Protected Header）のみで、一つの署名を適用できます。
JWS JSON Serialization はヘッダーに署名付きのものとそうでないもの（Unprotected Header）を持つことができ、複数の署名を適用できます。

### JWS Compact Serialization

JWS Compact Serialization の場合は以下のような構成になります。

```
BASE64URL(UTF8(JWS Protected Header = JOSE Header)) || '.' ||
BASE64URL(JWS Payload) || '.' ||
BASE64URL(JWS Signature)
```

まず、JOSE Headerを作成し、Base64URLエンコードします。
JOSE Headerとは、ヘッダーとペイロードに対する署名アルゴリズムを表現したJSONオブジェクトです。

https://tools.ietf.org/html/rfc7515#section-4
> For a JWS, the members of the JSON object(s) representing the JOSE
   Header describe the digital signature or MAC applied to the JWS
   Protected Header and the JWS Payload and optionally additional
   properties of the JWS.

`typ`でデータ構造がJWTによるものであることを明示し、`alg`で署名に使われるアルゴリズムを指定します。`alg`は必須です。

```json
{"typ":"JWT",
 "alg":"HS256"}
```

これをBase64URLエンコードします。

`eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9`

同様に実際に送りたいデータ（ペイロード）をBase64URLエンコードします。
データ形式は任意でこの例ではJSONオブジェクトを利用しています。

```json
{"iss":"joe",
 "exp":1300819380,
 "http://example.com/is_root":true}
```

`eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ`

次に、Base64URLエンコードしたJOSEヘッダーとペイロードをピリオドで連結したものに対して署名をし、これもBase64URLエンコードします。
`dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk`

最後に、Header.Payload.Signatureの順にピリオドで連結するとJWS（JWS Compact Serialization 形式）が完成します。

```
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
```

逆にこれを受け取って検証するときは、Base64URLデコードし、署名アルゴリズムがヘッダーから分かり、署名の検証を行います。

# JWTの使い所とセキュリティに関する問題

ここまで、JWTの概要について確認してきましたが、これらはどのような場合に利用されるべきものでしょうか？

結論から言うと、JWTはURLセーフな、一度使って終わりのようなトークンとしては利用できそうです。逆に、一定期間有効なログインセッションなどのセッションデータとして利用すべきではありません。

## セッション情報にJWTを利用した場合の問題

ログインセッションにJWTを利用するという話は聞くことがありますが、以下のような問題点があります。基本的に[この記事](http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/)がわかりやすくまとめられています。

ログインセッションにJWTを利用するとは、ログインしたユーザーの情報（ユーザーIDなど）をClaim SetとしたJWTをHTTPヘッダに付与してクライアント側のCookieにセットし、クライアントからリクエスト時にJWTもサーバーに送り、サーバー側はそれを元にどのユーザーからのリクエストであるかを特定すると行った仕組みです。

**1 . URLやCookieに格納できるサイズを超える可能性がある**

RFC6265ではCookieは、[少なくとも 4096 bytes のサイズの制限がなければならない](https://tools.ietf.org/html/rfc6265#section-6.1)とありますが、Claim Setが大きかったり、Base64URLエンコードをすることによってその上限を超える可能性があります。Cookieに格納できない場合はローカルストレージに格納するくらいしかありませんが、JavaScriptから操作が可能になり、HttpOnlyなどの恩恵が受けられなくなります。

**2 . 発行したJWTだけを失効できない**

セッション情報としてJWTを発行すると、その有効期限が切れるまでそのJWTだけを失効させる方法がありません。
そのため、アカウントが攻撃者に乗っ取られた場合や、変更のパスワードでログインした場合、ログアウトしている場合などでもJWTの有効期限を迎えるまで、そのJWTはそのユーザーのログイン情報として利用できてしまいます。
それに対処するには、JWSの場合であれば署名アルゴリズムに用いる鍵を変えることができますが、全てのそれまで発行したトークンが失効します。

一方で伝統的なKeyValueストアにセッション情報を保持し、それに対応するKeyからセッションIDを生成しクライアントに渡す方法であれば、KeyValueストアの対応するセッション情報を削除すれば有効期限に限らず失効させることができます。

**3 . 伝統的なセッション管理の方法に対して使い古されていない**

仮に上記のような問題が解決できたとしても、伝統的なセッション管理を実現する実装やライブラリはすでに多く利用されており、それに伴い多くのセキュリティ面での改善が加えられています。
そのような恩恵を受けられない点が差になります。


これらのことから、JWTは有効期限がかなり短いものや、一度利用されて終わりのもの（使い回されない）、機密性の低いサイズの小さい情報をURLセーフに送るものなどでは利用できそうです。


特に以下の記事を参考にさせていただきました。

- http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
- https://co3k.org/blog/why-do-you-use-jwt-for-session