JWT(JSON Web Token)を使った認証を試みる

Oauth2やOpenID Connectなどすでに導入されているJWT(JSON Web Token)。
今後IoTとかを考えると認証手法としては結構有効な認証方法だということで、改めて眺めてみた。

JSON Web Token

JSON Web Token(JWT) jot(ジョット)と発音する。
JSONを電子署名したurl-safe(URLで使用できない文字が含まれる)なclaimのことを指す。rfc7519
また同じような言葉もあるので一旦整理する。

  • JWS は署名したもの
  • JWE はEncryptしたもの

一般的にJWTというとJWSのことをいう。

電子署名(公開鍵+秘密鍵方式)をしているため、データの読み出し、書き込みができる。
しかし、その内容の改竄はチェックできる。
JSONの内容を秘匿化するわけではないので、内容は誰でも見ることはできるという点は注意が必要。

jwt.ioというサービスを使うことでdebugすることができるので、試してみると見ての通り、PAYLOADの内容は見ることができる。

JWTの形式は

1
[ HEADER ] . [ PAYLOAD ] . [ SIGNATURE ]

という形で構成され、HEADERとPAYLOADはbase64エンコードされている。


使い方として、認証時にトークンを発行し、必要に応じて検証することで認証済みとして扱うようにするとか。
同じことはSessionを使うことでもできるが、sessionを使う場合との違いは、サーバーが状態(state)を持たないでいいので、
token発行ホストと認証ホストが異なっていても問題ない。(まさにステートレス)
JWTを使うと、CSRFの攻撃を考えなくて良くなる。

また、JWTは

  • Base64エンコーダー・デコーダー
  • jsonパーサー
  • 署名検証機能

上記のがあれば利用できるため、汎用性が高い。

デバイスによってはjsonを保持しておけば、認証できるという意味ではIoTとかを考慮すると重宝する。
クライアント側で永続化はメモリキャッシュでも、DBでもブラウザのLocalStrageも良い。
認証が必要な時に、tokenをポストするかhttpヘッダーで送る形となる。

1
Authorization: bearer <token>

JSON Web Token の中身

タイプとハッシュアルゴリズムを保持しており、これをBase64している

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

HMAC SHA-256のJWTを表す。これはBase64で

1
2
Base64.encode64('{"alg": "HS256", "typ": "JWT"}')
# => eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9

となる。

PAYLOAD

JWTクレーム。トークンの本体となる部分で、幾つかのクレーム名はあらかじめ予約されている。
詳細はRFCを参照
クレーム名はJWT作成者側で自由に作成も出来る。

例えば以下のようなクレーム

1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

こちらもBase64で

1
2
Base64.encode64('{"sub":"1234567890","name":"John Doe","admin":true}')
# => eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

SIGNATURE

最終パートはSIGNATURE。HEADERとPAYLOADをSecretKeyで署名。

SecretKeyをsecretとすると、ここまでの結果から署名は

1
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

となる。

最終的な結果としてJWTは

1
eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

このtokenを使って認証を行うことなる。

アプリケーションで試してみた

Expressをつかって簡単なサーバーでJWT認証を試してみる。

jwt-express-example というExpress + MySQLなアプリケーションを作成して試してみる。

1
2
3
4
5
6
7
8
9
10
11
12
# dockerコンテナを起動して
# 任意のパスワードでユーザー作成
$ curl -X POST http://127.0.0.1:3000/user -d 'name=testuser' -d 'password=secretpassword'
{"id":2,"name":"testuser","password":"secretpassword","updated_at":"2016-08-03T14:02:08.000Z","created_at":"2016-08-03T14:02:08.000Z"}

# ユーザー認証をパスワードで行う
$ curl -X POST http://127.0.0.1:3000/authentication -d 'name=testuser' -d 'password=secretpassword'
{"token":"CREATED-JSON-WEB-TOKEN","message":"Authentication successfully finished."}%

# 別のホストでログインする
$ curl -X POST http://127.0.0.1:8000/login -d 'access_token=CREATED-JSON-WEB-TOKEN"'
{"id":2,"name":"testuser"}

という感じになる。

結論

ウェブサービスでの認証は複数のドメイン間で行われることが多い。そのため、クロスドメインを考える必要があり、
複雑化していくことなる。JWTのような機構を使うことで、サービス提供側はtokenを認証するということだに注力できるため、
仕様もシンプルにできる。
IoTなデバイスが増える中で、JWTな事案は今後もっと増えてくるんだろうなぁ。

参考にしたページ

Comments