`fast-jwt`は、生の公開JWK(JSON Web Key)をHMACのシークレットとして扱い、HS256トークンの偽造を可能にする。

### 概要

`fast-jwt`のバージョン6.2.4では、生のシリアライズされた公開JWKを、HMACのシークレットとして静的に分類する。

もしアプリケーションが公開JWKのテキストを検証キーとして提供し、HS256が明示的に許可されているか、または自動的に推測される場合、同じ公開JWKテキストを知っている攻撃者は、それをHMACキーとして使用し、`fast-jwt`が有効と認識する任意のHS256トークンを作成できる。

これにより、偽造されたJWTのクレームを通じて、認証または認可を回避できる可能性がある。

PoC(Proof of Concept:概念実証)は、シリアライズされたRSA公開JWKを使用してこの問題を実証している。同様のPEMではない分類は、生のJWKS(JSON Web Key Set)のテキストにも適用されるが、添付されたスタンドアロンのPoCは、最も単純なJWKのケースに焦点を当てている。

### 詳細

検証機能は、文字列またはBufferをキーマテリアルとして受け入れる。
`src/crypto.js`の`performDetectPublicKeyAlgorithms()`関数は、提供された文字列から許可されるアルゴリズムファミリーを推測しようとする。

サポートされているPEM形式の公開鍵と一致する文字列は、非対称鍵として分類される。その他のすべての非空文字列は、HMACのシークレットであると見なされる:

```js
function performDetectPublicKeyAlgorithms(key) {
const trimmedKey = key.trim()
const publicKeyPemMatch = trimmedKey.match(publicKeyPemMatcher)

if (trimmedKey.match(privateKeyPemMatcher)) {
throw new TokenError(
TokenError.codes.invalidKey,
'Private keys are not supported for verifying.'
)
} else if (
publicKeyPemMatch &&
publicKeyPemMatch[1] === 'RSA'
) {
return rsaAlgorithms
} else if (
!publicKeyPemMatch &&
!trimmedKey.includes(publicKeyX509CertMatcher)
) {
// Not a PEM, assume a plain secret
return hsAlgorithms
}

// ...
}

シリアライズされたRSA、EC(楕円曲線暗号)、またはOKP(最適鍵交換プロトコル)の公開JWKは有効なJSONであるが、PEM形式ではなく、証明書ヘッダーを含まない。したがって、以下の処理に到達する:

return hsAlgorithms

HS256の検証処理では、この完全な公開JSON文字列がHMACキーとして使用される。

JWKの公開鍵情報は、設計上公開されるため、攻撃者は検証者の暗号鍵情報を知ることができます。その公開情報がHMACの秘密鍵として再解釈されると、攻撃者は任意のクレームに対して有効なHS256署名を計算することができます。

脆弱なプロセスは以下の通りです。

公開非対称JWK JSON
|
v
PEM形式の検出に一致しない
|
v
プレーンな秘密鍵として分類される
|
v
HS256が許可または推測される
|
v
攻撃者が公開JSONのバイトを使用して署名を行う
|
v
偽造トークンが受け入れられる

添付のPoCは、関連する構成の両方を示しています。

明示的な混合ファミリーの許可リスト:
createVerifier({
key: rawJwk,
algorithms: ['HS256', 'RS256']
})
明示的なアルゴリズムの許可リストがない場合:
createVerifier({
key: rawJwk
})

2番目の構成では、fast-jwtは生のPEM形式でない文字列を対称鍵情報として検出し、HSアルゴリズムファミリーを許可します。

RS256のみを許可する検証者は、同じ偽造トークンを拒否し、ネガティブコントロールとして機能します。

createVerifier({
key: rawJwk,
algorithms: ['RS256']
})

ドキュメントでは、非対称検証鍵をPEMエンコードされた公開鍵として記述しています。問題点は、生のJWKが非対称鍵として正常に解析されることではなく、曖昧な構造化された公開鍵テキストが、拒否される代わりに、静かに対称鍵として再分類され、その分類が攻撃者によって制御されるHS256トークンを許可することです。

PoC

要件:

Node.js 20以降
npm
添付のZIPファイル

添付ファイルを展開し、以下のコマンドを実行します。

cd poc
npm install
node poc.js

PoCは、以下の手順を実行します。

ローカルで新しいRSA鍵ペアを生成します。
公開鍵のみをJWK形式でエクスポートします。
kid、use、algなどの通常の公開JWKメタデータを追加します。
公開JWKをJSON形式でシリアライズします。
シリアライズされた公開JWKテキストをHS256 HMAC鍵として使用します。
攻撃者が選択した管理権限を含むトークンを作成します。
同じ生のJSON文字列を実際のfast-jwt検証器に渡します。
HS256/RS256の両方を許可するホワイトリストで受け入れられることを確認します。
アルゴリズム検出が有効になっている状態で受け入れられることを確認します。
RS256のみの検証器で拒否されることを確認します。

期待される出力:

混合アルゴリズム:偽造トークンが受け入れられました
推測されたアルゴリズム:偽造トークンが受け入れられました
RS256のみの検証:偽造トークンが拒否されました
脆弱性:fast-jwt@6.2.4は、攻撃者によって署名されたHS256のクレームを受け入れます

受け入れられたトークンには、以下が含まれています。

{
"sub": "attacker",
"role": "admin",
"admin": true
}

このPoCは、新たに生成された鍵ペアを使用して完全にローカルで動作します。
本番環境のサービス、IDプロバイダー、またはサードパーティアプリケーションには接続しません。

影響

影響を受けたアプリケーションは、攻撃者が生成したJWTクレームを正当なものとして受け入れる可能性があります。

検証されたクレームの使用方法によっては、攻撃者は以下を偽造できる可能性があります。

ユーザーまたは主体識別子;
管理者権限;
認可範囲;
権限;
テナントまたは組織識別子;
および、アプリケーション固有の認可フラグ。

これにより、認証の回避、水平方向の権限昇格、垂直方向の権限昇格、または保護されたアプリケーションデータへの不正アクセスが発生する可能性があります。

脆弱性の悪用には、以下の条件すべてが必要です。

アプリケーションが、生のシリアライズされた公開JWKまたはJWKSのJSONテキストを検証器の鍵として渡す。
HS256が明示的に許可されているか、またはPEM以外の文字列から推測される。
攻撃者が、検証器に渡される正確なシリアライズされたバイトを把握している。

通常、公開されているJWK/JWKSの情報は、利用者がアクセスできるように公開されており、多くの場合、公開されている発見エンドポイントを通じて提供されます。しかし、プロパティの順序、空白、またはその他のシリアル化の違いが、攻撃者が正確なHMACキーのバイトを再現する能力に影響を与える可能性があります。

これらの統合およびシリアル化要件は、「高い攻撃の複雑さ」として評価されています。

サポートされているPEM形式の公開鍵を提供し、検証を想定される非対称アルゴリズムファミリーに制限するアプリケーションは、このPoC(概念実証)の影響を受けません。

以前のパッケージバージョンは、今回のレポートの一部として評価されていません。

推奨される対策:

構造化された公開鍵表現は、任意の非PEM文字列が対称鍵として分類される前に検出する必要があります。

少なくとも、非対称のJWKまたはJWKS構造を含むJSON文字列は、HS*の検証において拒否されるべきです。関連する非対称のktyの値には、以下が含まれます。

RSA
EC
OKP

考えられる対策としては、以下があります。

HMACの分類を行う前に、JSONのような検証文字列を解析する。
HS256、HS384、およびHS512において、非対称のJWK/JWKS構造を拒否する。
PEMの検出が失敗したという理由だけで、すべての非PEM文字列を対称の秘密鍵として扱うべきではない。
キーの形式が曖昧な場合、明示的なアルゴリズムの選択を必須とする。
許可されるアルゴリズムファミリーを、PEMの検出の成否ではなく、意味論的なキーの種類に紐付ける。

回帰テストは、以下の内容を網羅する必要があります。

コンパクトにシリアル化されたJWKのJSON。
整形されたJWKのJSON。
生のJWKSのJSON。
RSA、EC、およびOKPの公開鍵。
対称/非対称アルゴリズムの組み合わせの許可リスト。
デフォルトのアルゴリズムの検出。
非対称アルゴリズムのみを許可する設定。
末尾の空白文字および改行のバリエーション。
プロパティの順序およびシリアル化のバリエーション。
[fast-jwt-raw-jwk-hs256-submit.zip](https://github.com/user-attachments/files/30396435/fast-jwt-raw-jwk-hs256-submit.zip)

影響を受けるパッケージ:
- npm (JavaScript) fast-jwt: 影響あり = 6.2.4、6.3.0で修正済み。