ユーザー認証周りのAPIを作っていたのですが、その中でJWTの仕組みを使いました。使い方はシンプルで分かりやすく比較的簡単にセキュリティ向上を見込めると感じたのですが、日本語の資料がまだ少なく敷居が高い印象でした。使ってみたら予想以上に便利だったのでメモを共有します。
間違いなどありましたら指摘お願いします。
JWT概要
- JWTは「Json Web Token」の略
- JWTはjsonをトークン化する仕組み
- 改ざん出来ないjsonと思ってもらえればOK
Signed JWT
- JWTがサポートしている署名アルゴリズム
- HMAC : 共通鍵方式
- RSA : 公開鍵方式
- ECDSA : 楕円曲線暗号(あまり使われ得ていない)
運用
- 秘密鍵と公開鍵を作っておく
- 送りたいデータを秘密鍵でエンコードし、レスポンスとして返す
- 帰ってきたJWTをセッションに保存
- 受信側は公開鍵を使ってトークンをデコードし、必要なデータを取り出す
- 必要ならばトークンに有効期間を設けておく
JWTの構造
JWTは3つのパートから成り、全てbase64urlエンコードされている。
この3つのパートをドット(.)で繋げたものがJWTである
各パートについて
Header : このパラメータがJWTであることや、署名アルゴリズムを示す
{"typ":"JWT", "alg":"HS256"}
エンコード後
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
Claim Set : 内容
{"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}
エンコード後
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
Signature : 上記のHeaderとClaimを繋げてRSA等で署名したもの
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
JWTは上記のHeader、Claim Set、Signatureをドット(.)で繋げると出来上がる
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
実際にRubyで使ってみる
- ドキュメントが分かりやすくシンプルであるため、'jwt'というそのままのGemを使用した
gem 'jwt'
$ bunde install
使い方
- RSAの場合
require 'jwt'
# 秘密鍵生成
rsa_private = OpenSSL::PKey::RSA.generate(2048)
# 公開鍵生成
rsa_public = rsa_private.public_key
# sample data
payload = {
id: 1,
name: 'tanaka',
password: 'jau0328ura0jrdsf3'
}
# payload を暗号化 (秘密鍵でしかできない)
token = JWT.encode(payload, rsa_private, 'RS256')
# payload を復号化 (公開鍵でもできる)
JWT.decode(token, rsa_public, true, { algorithm: 'RS256' })
# 秘密鍵で復号化
JWT.decode(token, rsa_private, true, { algorithm: 'RS256' })