JWT(JSON Web Token)はドットで連結された 3 つの Base64url エンコード部分です。クレームを読むためのデコードは 1 回の関数呼び出しで済み、秘密鍵は不要 です —— そのため JWT のペイロードは機密データを置く場所ではありません。本ガイドでは JWT とは何か、各部分のデコード方法、そしてデコードと検証の決定的な違いを説明します。
JWT とは?
JWT はコンパクトで URL セーフなトークンで、当事者間でクレーム —— 通常はユーザの ID と権限 —— を運びます。RFC 7519 で標準化されています。トークンはこのように見えます:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFkYSJ9.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk2 つのドットで 3 つの部分に分割されます:header、payload、signature。
3 つの部分
- Header —— 署名アルゴリズムを記述する小さな JSON オブジェクト、例:
{"alg":"HS256","typ":"JWT"}。 - Payload —— claims の JSON オブジェクト。例:
sub(subject)、exp(有効期限)、その他のカスタムフィールド。 - Signature —— header と payload に対する暗号署名で、改竄検出のためのもの。人間に読めず、JSON でもありません。
header と payload は Base64url エンコードされた JSON です。署名は JWS(RFC 7515)で生成されます。
JavaScript で JWT をデコード
ドットで分割し、最初の 2 つを Base64url デコード。JWT はBase64url(+ と / の代わりに - と _、パディングなし)を使うので、デコード前に変換が必要です:
function decodeJwt(token) {
const [headerB64, payloadB64] = token.split('.');
const decode = (b64url) => {
const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
const json = atob(b64);
return JSON.parse(json);
};
return { header: decode(headerB64), payload: decode(payloadB64) };
} Unicode を完全に扱うには、Base64 をバイトにデコードしてから TextDecoder を通してください。atob だけではいけません。
Python で JWT をデコード
import base64, json
def decode_jwt(token):
header_b64, payload_b64, _sig = token.split('.')
def decode(part):
padded = part + '=' * (-len(part) % 4)
return json.loads(base64.urlsafe_b64decode(padded))
return decode(header_b64), decode(payload_b64)デコードは検証ではない
JWT で最も重要な点です。誰でも トークンをデ コードして payload を読めます —— 鍵不要。デコードはトークンが主張することを示すだけで、その主張が信用できるかを示しません。
トークンを信頼するには、信頼できるライブラリを使い、サーバの秘密鍵または公開鍵に対して署名を検証 しなければなりません —— 自作しないこと:
import jwt from 'jsonwebtoken';
// 署名検証 AND 有効期限チェック;無効なら投げる
const claims = jwt.verify(token, process.env.JWT_SECRET);
// jwt.decode(token) —— 検証しない;確認のみサーバでの検証こそが JWT を認証情報にします。デコード済み payload を検証なしに信頼するのは古典的な認証バグです。
JWT ペイロードは暗号化されていない
payload は単に Base64url エンコードされた JSON なので、機密性はゼロ です。これは Base64 は暗号化ではない と同じ誤解です:エンコードは誰でも逆にできます。
パスワード、完全なクレジットカード番号、機密の個人情報を JWT payload に入れてはいけません。本当に暗号化されたトークンが必要なら、JWE(JSON Web Encryption、RFC 7516)を使ってください —— 別の、はるかにマイナーな標準です。実トークンを信頼できないオンラインデコーダに貼るのも避けてください;機密 JSON をオンラインツールに貼るべきでない理由。
検証順序:署名が先、クレームは後
本番でトークンを検証するとき、順序は重要です。安全なチェックリスト:
- アルゴリズム許可リスト —— 期待集合(例:
['RS256'])以外は拒否。noneは決して受け入れない。 - 署名 —— 正しい鍵で検証(JWKS の
kidから検索)。 exp—— トークンが期限切れでないこと。nbf(not-before)—— 使用が早すぎないこと。iat—— 発行時刻が異常に未来でないか整合性チェック(時計ずれは別)。iss—— 発行者が期待する信頼できる権威であること。aud—— audience があなたのサービスを含むこと。
ほとんどの JWT ライブラリは 1-4 をデフォルトで行い、6-7 はオプトインを要求します。iss と aud がチェックされない限り、エコシステム内の別のサービス向けの妥当なトークンがあなたのサービスにリプレイされ得ます。
標準的なクレーム
| Claim | 意味 |
|---|---|
iss | Issuer —— トークンの発行者 |
sub | Subject —— トークンの対象者(多くはユーザ ID) |
aud | Audience —— トークンの宛先 |
exp | Expiry —— この Unix タイムスタンプ後は無効 |
iat | Issued-at —— トークン発行時の Unix タイムスタンプ |
nbf | Not-before —— この時刻まで無効 |
kid ヘッダと alg: none の落とし穴
実際の JWT に頻出する 2 つのヘッダフィールド、すぐに見分けられるようにしまし ょう:
kid(key ID) —— 鍵集合(JWKS)のどの鍵がトークンを署名したかを示すヒント。検証者が署名チェック前に正しい鍵を選べるようヘッダに置かれます。kidは信頼できない入力として扱ってください:固定の許可リスト(または取得済み JWKS)で検索し、ファイルパス、SQL パラメータ、URL として使わないでください。alg: "none"—— 妥当だが歴史的に危険なアルゴリズムで「署名なし」を意味します。デコーダは喜んでデコードし、寛容な検証者は受け入れてしまい、偽造ペイロードを「妥当」なトークンにします。明示的にalg: noneを拒否し、ライブラリは期待する正確なアルゴリズム(例:['RS256'])に固定して、トークン自身のヘッダ申告を信用しないでください。
ブラウザで JWT をデコード
トークンのクレームを素早く検査するには、任意の Base64url 部を fixjson の Base64 デコーダ に貼り —— ローカルで JSON にデコードし、サーバに送信しません。機密でないトークンには専用のデバッガでもよいですが、実ユーザデータを含むものにはローカル優先のツールが安全です。
よくある質問
JWT をデコードするには?
ドットで分割し、最初の 2 部分(header と payload)を Base64url デコードして JSON にします。第 3 は署名で人には読めません。デコードに鍵は不要です。
JWT のデコードと検証は同じですか?
違います。デコードはクレームを読むだけで誰でもできます。検証は鍵で署名をチェックしてトークンが本物で 改竄されていないことを確認します。トークンを信頼する前に必ずサーバで検証してください。
JWT のデータは誰でも読めますか?
はい。payload は Base64url エンコードであって暗号化ではありません —— トークンを持つ誰にとっても平文です。秘密を JWT payload に保管しないでください。Base64 は暗号化ではない。
JWT、JWS、JWE の違いは?
署名付き JWT は JWS(RFC 7515)を使います —— payload は読めて改竄検出可能。JWE(RFC 7516)は実際に payload を暗号化して機密性を提供。日常で見る普通の JWT は JWS です。
kid クレームは何に使う?
kid はヘッダフィールド(payload クレームではない)で、JWKS のどの鍵がトークンを署名したかを示し、検証者が正しい鍵を選べるようにします。あなたが制御する固定鍵集合で検索 —— ファイルパスや URL として信用しないでください。
なぜ alg: "none" は危険?
「署名なし」を意味します。トークンのアルゴリズム宣告を尊重する寛容な検証者は、偽造ペイロードを受け入れます。ライブラリを期待する正確なアルゴリズム(例:['RS256'])に固定し、none を明示的に拒否してください。
トークンをローカルで検査
- Base64 エンコード & デコード —— ブラウザで JWT の header/payload をデコード
- Base64 は暗号化ではない —— JWT payload が誰でも読める理由
- 機密 JSON をオンラインに貼るべきでない理由 —— 実トークンを安全に扱う
- RFC 7519:JSON Web Token —— 標準そのもの