署名付き JWT を作る方法
- アルゴリズム を選びます。ヘッダー(JSON) の
algフィールドは、選択に合わせて自動で変わります。 - ペイロード(JSON) を編集します。+ iat(現在時刻) は現在時刻を挿入し、+ exp(1 時間後) は 1 時間後を有効期限に設定します。どちらも JWT の仕様どおり秒単位です。
- HS256、HS384、HS512 なら シークレット を入力し、シークレットの形式 をテキスト、Base64、16進数のいずれかにします。ほかのアルゴリズムなら、秘密鍵を貼り付けるか 鍵ペアを生成 を押します。
- トークンをコピー するか、デコーダーで開く を押して確認します。デコード・検証 タブが開き、トークンと検証用の鍵が入力された状態になります。
読み込み時のサンプルは、シークレット base64hub-demo-secret で署名した有効な HS256 トークンです。クレームを 1 つ変えると、それに合わせて署名が変わる様子を確認できます。
アルゴリズムの選び方
| アルゴリズム | 鍵の種類 | 検証できる人 | トークン内の署名 |
|---|---|---|---|
| HS256 | 共有シークレット(HMAC-SHA256) | 同じシークレットを持つ人 | 43 文字 |
| RS256、PS256 | 2048 ビット以上の RSA 秘密鍵 | 公開鍵を持つ人 | 342 文字(RSA-2048) |
| ES256 | P-256 の EC 秘密鍵 | 公開鍵を持つ人 | 86 文字 |
| EdDSA | Ed25519 秘密鍵 | 公開鍵を持つ人 | 86 文字 |
同じバックエンドが確認するセッショントークンのように、1 つのサービスが署名と検証の両方を行うなら、HS256 がいちばん簡単です。2 つ目の当事者が検証する必要が出てきたら、HMAC のシークレットを共有すると相手もトークンを作れてしまうので、非対称アルゴリズムに切り替えて公開鍵だけを公開してください。ES256 と EdDSA は RSA よりずっと短いトークンになり、Cookie やヘッダーでは効いてきます。PS256 は PSS パディング方式を使う RSA です。すべての検証側が対応している場合だけ選んでください。
none は、パーサーのテスト用にリストに入れています。alg: none のトークンは署名が空で、それを受け付けるサーバーは誰にでもだまされてしまうので、ツールが警告を出します。
このエンコーダーが受け付ける鍵と生成する鍵
署名には、PEM PKCS#8 の鍵(BEGIN PRIVATE KEY)、PKCS#1 の RSA PRIVATE KEY、SEC1 の EC PRIVATE KEY、秘密鍵の JWK を使えます。生成ツールの代わりに OpenSSL でテスト用の鍵を作るなら:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out rsa.pem
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec.pem
openssl genpkey -algorithm ed25519 -out ed.pem
openssl pkey -in ec.pem -pubout -out ec.pub
鍵ペアを生成 は、アルゴリズムに応じて RSA-2048、P-256、P-384、P-521、Ed25519 の鍵を、WebCrypto を使ってブラウザ内で作ります。秘密鍵は PKCS#8 PEM、公開鍵は SPKI PEM で得られ、さらに kid が鍵の RFC 7638 サムプリントになった JWKS も得られます。JWKS は、検証側がダウンロードできるよう jwks_uri で配信するものです。
HS256 では、シークレットはバイト列として使われます。サーバーが環境変数からシークレットを読み、先に Base64 デコードしているなら、ここでも シークレットの形式 を Base64 にしてください。そうしないと署名が一致しません。Base64 鍵生成ツール の JWT シークレットのプリセットで作った値は Base64URL で、デコードすると 32 バイトになります。
ペイロードに入れるクレーム
実用的なアクセストークンには、通常 iss(発行者)、sub(対象者)、aud(どの API 向けか)、iat、短い exp を入れます。個別のトークンを失効させる必要があるなら jti を追加してください。時刻は 1970 年からの秒数(整数)です。JavaScript で作るなら、Date.now() はミリ秒を返すので Math.floor(Date.now() / 1000) を使います。
ペイロードは署名されるだけで、暗号化されることはありません。トークンを見た人は誰でも JWT デコーダー でデコードできるので、パスワード、API キー、ログに出したくない個人情報は入れないでください。
コードで JWT に署名する
形式は手で組み立てられるほど単純で、ライブラリをデバッグするときに役立ちます。この Node.js の関数は、同じ入力に対して PyJWT の jwt.encode(payload, secret, algorithm="HS256") とまったく同じトークンを生成します。
const crypto = require("crypto");
const b64url = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
function signHS256(payload, secret) {
const input = b64url({ alg: "HS256", typ: "JWT" }) + "." + b64url(payload);
const sig = crypto.createHmac("sha256", secret).update(input).digest("base64url");
return input + "." + sig;
}
console.log(signHS256({ sub: "user-42", iat: 1767225600, exp: 1767229200 },
"replace-me-with-32-random-bytes!"));
出力は eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTQyIiwiaWF0IjoxNzY3MjI1NjAwLCJleHAiOjE3NjcyMjkyMDB9.F3xnXnXW8nUQy-Ay37DF626Lb5lHPKI3IFvqoEf3mTw です。このページで同じヘッダー、ペイロード、シークレットを入力すれば、同じ文字列が得られます。ツールは署名の前に JSON を詰めるのでインデントは関係ありませんが、キーの順序は関係します。署名は正確なバイト列に対して計算されるからです。本番環境ではメンテナンスされているライブラリを使ってください。エンコードの面については Node.js の Base64 をご覧ください。
よくある質問
HS256 のシークレットはどれくらいの長さにすべきですか?
最低 256 ビット、つまりランダムな 32 バイトです。RFC 7518 は、ハッシュの出力と同じ長さ以上の鍵を求めています。パスワードのような短いシークレットは、トークン 1 つからオフラインで総当たりできてしまうので、Base64 鍵生成ツール で生成してください。
HS256 と RS256 のどちらを使うべきですか?
同じサービスがトークンを発行して検証するなら、両者が 1 つのシークレットを共有する HS256 です。ほかの当事者が検証する必要があるなら RS256、ES256、EdDSA です。相手には公開鍵だけを渡すので、相手がトークンを作ることはできません。
秘密鍵でエラーが出るのはなぜですか?
パスワードで保護された鍵(BEGIN ENCRYPTED PRIVATE KEY)は受け付けません。openssl pkey -in key.pem -out plain.pem でパスフレーズを外し、その結果を貼り付けてください。鍵の種類もアルゴリズムに合っている必要があります。EC の鍵で RS256 の署名はできません。
トークンに kid を追加するには?
鍵ペアを生成 の後、JWKS の出力から kid をコピーし、ヘッダーの JSON に "kid": "…" のように追加してください。検証側はこれを使って、鍵セットから正しい鍵を選びます。