ReDoS in verify() when audience option is a RegExp: attacker-controlled aud claim → catastrophic backtracking
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 48/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- javascript, node.js
- Ambito
- authentication, backend, security
Direzione di ricerca
Inizia in verify.js, intorno al ciclo di controllo di audience, ed esegui la riproduzione fornita e la property di fast-check per osservare il ritardo. Esamina la documentazione correlata all’opzione audience in README e verify.d.ts. Il lavoro è completo quando il percorso audience RegExp ha un comportamento limitato e documentato per claims controllati da un attaccante e lo scenario di regressione è coperto.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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:
- Doesn't limit the length of
audbefore RegExp.test. - Doesn't surface the ReDoS risk in
verify's docs for the RegExp-audience path. - 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+
- Lingua principale
- JavaScript
- Stelle
- 18.2k
- Fork
- 1.3k
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di auth0/node-jsonwebtoken
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
auth0/node-jsonwebtoken#1042 · 1 commento ·
-
`jwt.sign()` callback is executed twice for "The payload already has an "..." property" errors Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
auth0/node-jsonwebtoken#1000 · 2 commenti · 1 reazione ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
auth0/node-jsonwebtoken#1048 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 65/100
auth0/node-jsonwebtoken#1046 ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 10/100
auth0/node-jsonwebtoken#1034 ·
Tutte le issue di auth0/node-jsonwebtoken
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
TheOdinProject/curriculum#31423 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
WGBH-MLA/dream-aapb#69 ·
-
Difficoltà 1/5 1-3 ore Idoneità per principianti 78/100
Mintplex-Labs/anything-llm#6490 ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 86/100
openlayers/ol-cesium#1364 ·