Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

ReDoS in verify() when audience option is a RegExp: attacker-controlled aud claim → catastrophic backtracking

オープン
#1,031 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

@yfwmaniish がすでに取り組んでいます。

2026年9月1日 から。

  • #1044 @yfwmaniish による — オープン

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
48/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
静か
技術スタック
javascript, node.js

調査の方向性

verify.js の audience-check ループ付近から始め、提供された再現手順と fast-check property を実行して遅延を観察します。README と verify.d.ts にある関連する audience オプションのドキュメントを確認します。完了条件は、audience RegExp パスが攻撃者に制御された claims に対して、制限され文書化された動作を持ち、回帰シナリオがカバーされていることです。

索引モデルが issue の本文から書いたものです。

説明

Description

jwt.verify(token, secret, { audience: regex }) passes the attacker-controlled aud claim of the JWT directly into RegExp.test(). If the application uses a regex with vulnerable patterns (nested quantifiers, ambiguous alternation), the verification call hangs for seconds-to-minutes per request — classic ReDoS via the audience claim.

Reproduction (jsonwebtoken 9.0.2)
const jwt = require('jsonwebtoken');
const SECRET = 'test-secret';

// Application uses RegExp for audience matching (documented feature)
const audRegex = /(a+)+$/;

// Attacker crafts a token with a malicious `aud` claim and signs it
// (in real scenarios the attacker controls some path that signs user-supplied audiences)
const token = jwt.sign({ aud: 'a'.repeat(25) + '!' }, SECRET);

const t0 = Date.now();
try { jwt.verify(token, SECRET, { audience: audRegex }); } catch (_) {}
console.log('verify took', Date.now() - t0, 'ms');
// Output: ~3000ms with 25 a's; ~12s with 27; ~93s with 30.
Property that fails
import fc from "fast-check";
import jwt from "jsonwebtoken";

const SECRET = 's';
const audRegex = /(a+)+$/;

fc.assert(fc.property(
    fc.integer({min: 5, max: 30}),
    (n) => {
        const tok = jwt.sign({ aud: 'a'.repeat(n) + '!' }, SECRET);
        const t0 = Date.now();
        try { jwt.verify(tok, SECRET, { audience: audRegex }); } catch (_) {}
        return Date.now() - t0 < 500;   // < 500 ms for any small input
    }
));
// Shrinks to n=22 ~ 25
Threat model

Many real applications use RegExp for audience matching (multi-tenant subdomains: /^https:\/\/[^.]+\.example\.com$/, wildcard tenants, microservice families). Where the aud claim originates from anything other than a hard-coded list — for example, a federated token-exchange endpoint where one party signs a token containing the next service's name — an attacker who controls that audience string can supply a payload that exhausts a CPU core per call.

Concrete impact: a single ~80-byte JWT request blocks an event-loop thread for ≥10 seconds. A few requests/second saturate the server.

This is the same class as CVE-2024-21534 (jsonwebtoken older), CVE-2024-21501 (sanitize-html), CVE-2026-35041 (fast-jwt's identical issue with allowedAud).

Root cause

verify.js, audience-check loop (around lines 194-207):

const match = target.some((targetAudience) => {
  return audiences.some((audience) => {
    return audience instanceof RegExp
      ? audience.test(targetAudience)    // <- no length cap, no timeout
      : audience === targetAudience;
  });
});

The library:

  1. Doesn't limit the length of aud before RegExp.test.
  2. Doesn't surface the ReDoS risk in verify's docs for the RegExp-audience path.
  3. Trusts the application to have audited its audience regex — but the attacker, not the application, supplies the input to that regex.
Suggested fix

In verify.js, before calling audience.test(targetAudience), enforce a configurable max-length on targetAudience (e.g. 256 chars by default). Throw JsonWebTokenError('audience claim exceeds length limit') on overflow.

Additionally, document the ReDoS hazard for the regex-audience path in README and verify.d.ts, and recommend re2 or static-regex-safety checking for application-supplied audience regexes.

Environment
  • jsonwebtoken: 9.0.2 (latest on npm)
  • Node: 20+
主要言語
JavaScript
スター
18.2k
フォーク
1.3k
PR マージ指標
30日以内にマージされた PR はありません

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

auth0/node-jsonwebtoken のほかの issue

auth0/node-jsonwebtoken の issue をすべて見る

似ている issue

JavaScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。