JWT の仕組みと暗号を安全に解読する方法
JSON Web Tokens の構造、署名の作成と検証方法、一般的な主張、セキュリティの落とし穴、JWT の解読方法を理解する。
· 8 分で読めます
JSON Web トークン (JWT) はどこにでもあります。OAuth と OpenID Connect ログイン、APIゲートウェイ、マイクロサービス間のシングルサインオン、モバイルアプリセッションなど。誤解されているのです。開発者はトークンを解読し、読み取れるJSONを見て、それがセキュリティ上の問題かどうか疑問に思う。この記事では、JWT が何であるか、その検証の方法、そして危険を引き起こすことがないように検査することが具体的に説明されます。
JWT の 3 つの部分
JWTはドットで連結された 3つのランダムな文字列のように見えます
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTkwMDAwMDAwMH0.Kx8...
各部分はBase64URLでコードされています。
- ヘッダー トークンタイプと署名アルゴリズムを記述するJSON、例えば
{"alg":"HS256","typ":"JWT"}。 - ペイロード JSON 含有クレーム:
{"sub":"42","exp":1900000000}のようなユーザーとトークンに関する声明。 - 署名 秘密またはプライベートキーのヘッダーとペイロードから計算されたバイト。
Base64URLは、+と/の代わりに-と_を使用し、パッディングを落とす[Base64]の変種である。
重要なことはヘッダーとパイロードが暗号化されてないだけです。持ってる人は読める。[JWT解読器] ((/tools/jwt-decoder) にトークンを貼ると、ペイロードが即座に表示されます。
署名の仕組み
署名が JWTを信頼できるものなのです。発行者は暗号化されたヘッダーとペイロードを取りそれを点で結合し文字列にサインします
HS256 (SHA-256のHMAC) で、発行者と検証者は1つの秘密鍵を共有します。
signature = HMAC-SHA256(secret, base64url(header) + "." + base64url(payload))
RS256または ES256では、発行者はプライベートキーでサインし、誰でも対応するパブリックキーで確認できます。通常はhttps://login.example.com/.well-known/jwks.jsonのようなJWKSエンドポイントで公開されます。多くのサービスがトークンを検証する必要があるが、一つだけ発行すべき場合、非対称アルゴリズムが好ましい。
サーバがトークンを受信すると、署名を再計算または検証します。攻撃者が"role":"user"を"role":"admin"に編集したとします。署名がもはや一致せず、トークンが拒否されます。
登録された請求事項
JWT仕様 (RFC 7519) は、標準的な主張名を定義している。
iss(発行者):トークンを作成した人、例えばhttps://login.example.com。sub(主体):トークンが誰のことなのか、通常はユーザーIDです。aud(オーディエンス):トークンが誰に向けられているか、例えばapi.example.comexp(有効期限):トークンが拒否される時間、[Unix秒] ((/blog/unix-timestamp-explained)。nbf(以前ではない):トークンが有効でない時間。iat(発行日時):トークンが作成されたとき。jti(JWT ID): 撤廃リストに役立つユニークな識別子。
申請は独自の主張を追加します:scope、roles、tenant_id、email。要求がすべて要求と共にやってくるのです
トークンを正しく検証する
サーバーは次の順序で、次のすべてのチェックをしなければならない:
- アルゴリズムはあなたが期待するものです.
["RS256"]のような許可リストを設定します。トークンのヘッダが自由に決定することを決して許さない。攻撃者はnoneに切り替えるか、HMACの秘密として公開鍵を使用するようにサーバーを騙す。 - 信頼された鍵セットから
kidが選択した正しい鍵で expは未来で、nbfは過去で、小型の30~60秒の時計偏差が許される。issはあなたのIDプロバイダーと完全に一致しますaudには あなたのサービスの識別子が含まれていますので、別の API に発行されたトークンは、あなたの API に再プレイできません。
管理されたライブラリ jose for JavaScript、PyJWT for Python、golang-jwt for Go を使って、自分で検証を書くのではなく、
解読と検証
解読とはBase64URLで最初の2つの部分を解読し JSONを解析することを意味します。鍵は必要ないし信頼性も証明できない。これは、以下に役立つ:
- APIが 404 を返した理由をデバッグする:
audは正しいのか?代金期限が切れたか? - ログインフローが実際に与える範囲や役割を確認します
- トークンリフレッシュで新しい
expが生成されたことを確認します
確認とは、上記のように署名と主張を検証する。認証決定はサーバーのみで検証後に行うべきです
フロントエンドコードは、ユーザーの名前を表示するためにトークンを解読したり、expの前にリフレッシュをスケジュールしたりできますが、ユーザーがブラウザで実行されているものを変更できるため、セキュリティのために解読された主張に依存してはならない。
ブラウザでトークンを保存する場所
完璧な答えはない妥協だけ
- HttpOnly、Secure、SameSite クッキーは JavaScript によって読み取れません.これはクロスサイトスクリプトによるトークン盗難から保護します。CSRFの保護が必要ですが、SameSite=LaxまたはStrictはほとんどのケースをカバーします。
- メモリ (JavaScript変数) は持続性から保護されていますが、再起動時に失われます。HttpOnly クッキーのリフレッシュトークンと組み合わせてください。
- localStorageは便利ですがページ上のどのスクリプトでも読み取れます
ほとんどのWebアプリでは、メモリ内の短期間のアクセストークンと HttpOnlyクッキーのリフレッシュトークンは良いバランスです。
期限及び撤回
JWTはステートレスで、サーバーはデータベース検索なしで検証できます。有効なトークンは期限が切れる前に簡単に取り消すことはできません。緩和策
- アクセス・トークンを保存する短命 5〜15分は一般的です
- 新しいアクセストークンを発行するために、安全に保存され、データベースで確認されたリフレッシュトークンを使用します。
- 高リスクのアクションでは、
jti値の撤回リストまたはユーザー1人当たりの"後有効トークン"タイムスタンプを確認してください。
よくある間違い
- 暗号、APIキー、個人データは、トークンを所有する誰でも読み取れます。機密性を確保したい場合は JWE (暗号化されたトークン) を使用するかサーバー側でデータを保管してください
- 非常に長持ちするトークンです. 1年間有効な盗難トークンは1年間の侵害です。
- オーディエンスチェックをスキップする.
aud検証がなければ、低特権サービスのトークンは高特権サービスに対して使用できます。 - **HMACの弱い秘密.**HS256の秘密は長くてランダムで、少なくとも256ビットでなければならない。簡単な秘密は単一のトークンでオフラインで暴力を振るうことができます
- ** ログトークン.** アクセスログとエラートラッカーでは、
Authorizationヘッダが捕捉されます。書き直して
トークンを安全に検査する
ブラウザでローカルで実行されるデコーダーを使用してください。スクリーンショットを共有したりチケットに貼り付けるときには、有効期限が切れたトークンやテスト環境トークンを好みます。チェック:
algとkidのヘッダはプロバイダーの設定と一致しますissとaudは APIが期待する値と一致しますexp、iatとnbfは現在の時間に対して意味があります- 作用域と役割には操作に必要なものがあります
まとめ
JWTはJSONクレームの署名された、暗号化されていないバンドルです。セキュリティはサーバーの署名確認とクレーム検証から完全に生じます。解読は無害でデバッグには有用で検証なしに解読されたデータを信頼することは真のリスクです。トークンが短命で機密のない小容量でトークンがアルゴリズム、署名、有効期限、発行者、視聴者を検証し機械に保存するツールでトークンを検査します
このページは英語から自動翻訳されています。誤りを見つけた場合はお知らせください。