[2023-01-31 12:00 JST 更新] JWTのシークレットポイズニングに関する問題

A pictorial representation of the JsonWebToken vulnerability.

This post is also available in: English (英語)

更新

2019年1月30日 PST

本脆弱性の悪用シナリオの前提条件に関するコミュニティからのフィードバックを受け、私たちはAuth0と協力してCVE-2022-23529を撤回することを決定しました。 

本稿で解説したセキュリティの問題はJsonWebTokenライブラリが安全でない方法で使用された場合には依然として懸念されるものです。そのシナリオでは、すべての前提条件を満たせばこの問題を悪用できる可能性があります。私たちは、その場合のリスクの大元はライブラリ側でなく呼び出し側のコードにあることに同意します。

この問題に対処するためJsonWebTokenのコードには重要なセキュリティチェックが追加されました。 

jsonwebtoken 8.5.1以前のバージョンをお使いの場合は最新版の9.0.0にアップデートすることをお勧めします。最新版では同セキュリティ問題を含む問題を修正済みで、より安全なコードを提供して本ブログで紹介したパッケージの悪用を防ぎます。 

本件に取り組んでいただいたAuth0と関心とフィードバックを寄せていただいたセキュリティコミュニティの皆さまに感謝を申し上げます。またGitHubからのご協力にも感謝します。本件の更新はAuth0 GitHubでお読みいただけます。.

2023年1月12日 PST

コミュニティからのフィードバックを受け、私たちは潜在的なエクスプロイトに関する詳細をもう少し明らかにしておくことにいたしました。初稿には「攻撃者はシークレットマネージャーをコントロールする必要がある」という記述がありましたが、このことをより明確にする説明や関連の図解が必要と判断したためです。

概要

Unit 42のリサーチャーは広く使われているオープンソースプロジェクトJsonWebTokenに新たな脆弱性を発見しました。本脆弱性の深刻度評価は高(CVSS 7.6)で、CVE識別番号はCVE-2022-23529です。

この脆弱性を攻撃者が利用した場合、JSON Webトークン (JWT)リクエストを悪意を持って細工することにより、そのWebトークンを検証するサーバー上で、リモートコード実行 (RCE) を実現できます。この脆弱性をエクスプロイトするには、いくつか前提条件が揃っている必要があります。このため攻撃者が野生で(in the wild)この脆弱性を使う可能性は低くなっています。

お使いのJsonWebTokenパッケージがバージョン8.5.1またはそれ以前の場合は、本脆弱性に対する修正を含むJsonWebTokenパッケージのバージョン9.0.0またはそれ以降に更新してください。

JsonWebTokenはオープンソースのJavaScriptパッケージで、これを使うとJWTを検証/署名できます。JWTの主な利用用途は認証と認可で、開発と保守はAuth0が行っています。本稿執筆時点で本パッケージは週次900万回以上のダウンロード、2万件以上の依存関係(JsonWebTokenのページによる)を持ちます。このパッケージはさまざまなアプリケーションの認証・認可機能で大きな役割を果たしています。

パロアルトネットワークスのお客様は、JsonWebTokenパッケージの脆弱なバージョンを実行しているアセットをPrisma Cloudを使って特定し、スキャン結果から関連するCVEを特定できます。

関連するUnit 42のトピック  CVE-2022-23529, remote code execution, open source, cloud

目次

JWT入門
認証のしくみ
JWTとオープンソース
脆弱性(CVE-2022-23529)
エクスプロイトの前提条件
JsonWebTokenの修正
開示プロセス
結論

JWT入門

JWT (ジョットと発音)はJSONデータをエンコード・署名することで安全に情報を転送する方法を定義したオープンスタンダードです。JWTはドット(.)で区切られた3つの部分からなる文字列構造を持っています。

ヘッダ.ペイロード.署名

JWTは任意の情報の伝達に使えますが、主な用途は「ある主体に関するひとまとまりの情報」を伝達することです。この情報は「クレーム(claim)」と呼ばれます。これは実際には「あるユーザーに関する有益な情報」を含むことが多いでしょう。

JWTのもっとも一般的な用途は認証(authentication)と認可(authorization)です。ここでは簡単にJWTの構造をおさらいしておきます。

JWTヘッダ

たいていのヘッダは「署名アルゴリズム」と「トークンの種類」を示す2つのパラメータで構成されています(図1)。

画像1は中括弧で囲まれた2行のコードのスクリーンショット。1行目は「"alg": "HS256"」で2行目は「"typ": "JWT"」
図1. JWTヘッダ

JWTペイロード

ペイロード(図2)はトークンの2番目の部分で、ここにクレームが含まれます。たいていはユーザーに関する有益な情報を提供します。

画像2は中括弧で囲まれた2行のコードのスクリーンショット。1行目は「"alg": "HS256"」で2行目は「"typ": "JWT"」
図2. JWTペイロード

クレームには「予約済みクレーム」、「パブリッククレーム」、「プライベートクレーム」の3種類があり、その詳細はRFC7519に記載されています。

ヘッダとペイロードはそれぞれBase64Urlでエンコードされ、JWT文字列(ヘッダ.ペイロード.署名)の1番目の部分と2番目の部分になります。

この例ではJWTヘッダがeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9、JWTペイロードが eyJ1c2VyX25hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlfQになります。

その後、JWTの3番目の部分である署名を秘密鍵を使って次の式で計算(署名)します。署名はトークンが偽造や操作をされていないことを確認するために使います。秘密鍵で署名しているので送信者の真偽を検証できます。以下は署名の計算例です。

base64Url(HMACSHA256(base64Url(header) + "." + base64Url(payload), secret_key)) = epQym-1JoN9eep458VBZw4-cVhwfmsI1cfaGa6PE818

各部分をまとめると図3に示すようなJWTが完成します。

画像3はJSONWebトークン文字列のスクリーンショット。ヘッダ、ペイロード、署名の各部分がそれぞれ赤、緑、青緑で表示されている。
図3. 完成したJSON Webトークン

認証のしくみ

ここで、JSON Webトークンを使う簡単な認証処理を見てみましょう(図4)。

  1. ユーザーは保護されたリソースにアクセスするため認証情報(通常はユーザー名とパスワード)を使ってログインします。
  2. この情報を含むリクエストが認証サーバーに送信されます。
  3. 認証サーバーがリクエストで送信された情報を検証し、秘密鍵で署名したJWTを発行します。秘密鍵で署名したJWTはサーバー上に保存してもよいしシークレットマネージャを使って別の場所に保存してもかまいません。
  4. これ以降、各ユーザーのリクエストにはAuthorizationヘッダ内にJWTが含まれるようになります。こうすることで、適切な権限を持つユーザーは保護されたリソースへのアクセスを受けられます。
  5. ユーザーが保護されたリソースへのアクセスをリクエストすると、JWTを含むリクエストがアプリケーションからJWT認証サーバーに対して生成されます。
  6. Authorizationヘッダ内で送信されたJWTの秘密鍵による検証が済むと、リクエストしたリソースに対するアクセスがユーザーに提供されます。この処理は、途中で改ざんされていないこと、そのユーザーはリクエストした情報を閲覧する正当な権限を持っていることを確かめるために行います。node.jsのJsonWebTokenパッケージではこの処理を verify()関数で行います。
画像4はユーザーとその認証情報の提供から始まるJWTの認証プロセスを示した図です。JWT発行サーバーから認証サーバーへの流れを示しています。
図4. JWTを使った認証フロー

JWTとオープンソース

オープンソースプロジェクトのおかげで、多くの組織は時間を含むさまざまなリソースを節約でき、これがスマートですばやい問題解決につながることもあります。こうしてJWTが多くの組織が頼りにするごく一般的な技術となったことは、JWTを実装するいくつかのオープンソースプロジェクトが大きな成功を収めた要因の1つにもなっています。

そうしたプロジェクトの1つにJsonWebTokenがあります。JsonWebTokenは、JWTの署名・検証・復号を行う著名なJavaScriptソリューションです。このツールはOktaの一部となったAuth0が開発・保守しています。

脆弱性(CVE-2022-23529)

典型的なJWTへの攻撃は、さまざまな偽造技術を使って不具合のあるJWT実装を悪用します。この種の攻撃による影響は深刻になりがちです。攻撃が成功すれば、攻撃者が認証・認可のしくみを回避して機密情報にアクセスしたり、データを窃取・改ざんしたりできる場合が多いからです。

JsonWebTokenパッケージが提供するメソッドの1つにverifyがあります。verifyメソッドは3つのパラメータ、tokensecretOrPublicKeyoptionsを受け取ります。この関数はJWTの有効性を検証し、復号したペイロード部を返します。

こちらのドキュメントによると、secretOrPublicKeyには文字列かバッファを指定することになっています[訳注: 本稿の調査が行われた2021年7月以前のReadMe.mdの内容より。2022年11月29日のReadMe.md以降は文字列とBufferのほかにKeyObjectも指定可能となっていますが、本稿ではKeyObjectについては触れません]。JsonWebTokenのverify.jsのソースコードには、以下図5のような行があるのが確認できます。

画像5はverify.jsから引用した4行のコードのスクリーンショット。secretOrPublicKeyが含まれている
図5. verify.jsからのコードスニペット

optionsのアルゴリズム一覧で許可されたアルゴリズムが提供されなかった場合、secretOrPublicKeyパラメータの提供するPEM(Privacy Enhanced Mail)ファイル内の値が代わりに割り当てられることになります。これには問題があります。secretOrPublicKeyが有効なPEMファイルの内容かどうかを確かめず、この未検証オブジェクト側のtoString()メソッドをそのまま使ってしまっています。このオブジェクトをコントロールできる攻撃者は、自分のtotSringメソッドを渡せます。このメソッドは、JsonWebTokenのverify(114行目)で実行されます。

node.js(JavaScriptランタイム環境の1つ)のバージョン18.9.1とJsonWebTokenパッケージのバージョン 8.5.1を使ってverify関数に悪意のあるオブジェクトをsecretOrPublicKeyパラメータを介して渡すというシナリオで、そのtoString()メソッドをオーバーライドした場合、何が起こるかを見てみましょう。

画像6は、リサーチャーがJsonWebTokenを使って単純なJWTトークンを作成し、悪意のあるオブジェクトを作成してそれをverify関数に渡し、その結果を確認しているコード行を含むスクリーンショット。
図6. 任意のファイルを書き込む概念実証コード(PoC)

私たちの悪意のあるコードはverify関数内で.includes(‘BEGIN CERTIFICATE’)をチェックする前に実行されてnodeプロセスを終了し、任意のファイルをホストマシン上に書き込みます。

画像7は書き込まれた任意のファイルのタイトルバーとその内容を表示したスクリーンショット。ファイル名は
図7 PoCの結果ファイルが作成された

同じ手法でRCEも実現できますが、それにはchild_processモジュールでペイロードを若干修正する必要があります(図8)。

画像8はchild_processモジュールでペイロードを修正した3行のコードのスクリーンショット。
図8. RCEのPoC

シークレットは認証サーバーの外部に保存できます。たとえば、別の場所にあるシークレットマネージャへの保存も可能です。シークレットマネージャへの書き込み権限だけをもっている攻撃者は、この時点で認証サーバー上でコードを実行できる状態です。オブジェクトが有効なシークレットかどうかを確認するしくみがあった場合、このコード実行はできません。

エクスプロイトの前提条件

前節では侵害された秘密鍵がどのようにRCEにつながるかを示しました。実際には、秘密鍵の保持・管理には、暗号化以外にシークレットマネージャの利用や秘密鍵のローテーションといったベストプラクティスが行われることが多いでしょう。 

本稿で解説した脆弱性をエクスプロイトしてsecretOrPublicKeyの値をコントロールしたければ、攻撃者はシークレット管理用のプロセスに内在する不具合を利用する必要があります。その場合でもシークレットマネージャからの出力が不明で、エクスプロイトは複雑になる可能性があります。これがエクスプロイトに適さない型(たとえば文字列)であるなど、さまざまな可能性があります。

脆弱性の複雑度に鑑みて私たちはCVSSスコア6.6を提案し、最終的には先方チームの計算で提案をいただいたCVSSスコア7.6に同意しました。

JsonWebTokenの修正

JsonWebTokenバージョン 9.0.0は以下の修正を含んでいます。

画像9は複数行のコードのスクリーンショットです。バージョン9.0.0でリリースされたJsonWebTokenの修正。secretOrPublickeyの型がチェックされるようになった
図9 修正

脆弱性のあるコードを削除し、secretOrPublickeyパラメータの型チェックを行って、secretOrPublicKeyに不正なオブジェクトが含まれないようにしました。

開示プロセス

  • 2022年7月13日 - Unit 42のリサーチャーが「責任ある開示」手順にのっとりAuth0チームに開示を送付
  • 2022年7月27日 - Auth0チームが同問題を「レビュー中」に更新
  • 2022年8月23日 - Unit 42リサーチャーが更新依頼を送付
  • 2022年8月24日 - Auth0チームが同問題を「エンジニアリングチームによる解決に向けた取り組み中」に更新
  • 2022年12月21日 - Auth0エンジニアリングチームが修正を提供

結論

こんにち、オープンソースプロジェクトはさまざまなサービスやプラットフォームのバックボーンとして広く利用されています。認証・認可プロセスに大きな役割を果たすJWTのような機微なセキュリティメカニズムの実装にもこうした利用は広がっています。

オープンソースソフトウェアの使用にあたっては、セキュリティ意識が非常に重要です。広く使われているセキュリティ系メカニズムのオープンソース実装のレビューは、そうしたメカニズムに対する信頼を保つうえで不可欠ですし、オープンソースコミュニティが参加できる活動の対象でもあります。

パロアルトネットワークスは、オープンソースソフトウェアのセキュリティ向上に向けた取り組みの一環として、オープンソースプロジェクトのセキュリティ脆弱性の特定を含むセキュリティ調査活動を定期的に行っています。

パロアルトネットワークスのPrisma Cloudをご利用中のお客様は[Vulnerabilities (脆弱性)]タブ以下から影響を受けるイメージやホストを検出できます。Prisma CloudプラットフォームはJsonWebTokenパッケージを検出し、脆弱なバージョンを動作させているエンティティがあればアラートを発報します。またVulnerability Explorer(脆弱性エクスプローラ)でCVE-2022-23529を検索すると脆弱性の詳細や影響を受けるアセットを確認できます。

Auth0チームによるプロフェッショナルな開示処理と、報告された脆弱性に対する修正のご提供に感謝いたします。

2023-01-20 09:15 JST 英語版更新日 2023-01-12 15:31 PST の内容を反映

2023-01-31 12:15 JST 英語版更新日 2023-01-30 07:20 PST の内容を反映