Bug: clockTolerance accepts arbitrarily large values, bypassing exp verification entirely
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 52/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, tracciando la validazione di clockTolerance e i confronti di exp/nbf. Conferma con i maintainers il limite superiore supportato e il comportamento degli errori, quindi aggiungi una copertura di regressione mirata per una tolleranza eccessiva e per entrambi i controlli. Il lavoro è completo quando i valori elevati vengono rifiutati senza indebolire la gestione normale del clock-skew.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
The clockTolerance option in verify() accepts any positive integer with no upper bound validation. Passing Number.MAX_SAFE_INTEGER (or any value large enough that exp + clockTolerance overflows to Infinity) causes the expiry check to silently pass for any expired token, regardless of how long ago it expired.
Environment
jsonwebtokenversion: 9.0.3 (latest)- Node.js: v20+
Reproduction
const jwt = require("jsonwebtoken");
const SECRET = "supersecret";
// Sign a token that expired 1 year ago
const expiredToken = jwt.sign(
{ sub: "user", role: "admin" },
SECRET,
{ expiresIn: "-365d" }
);
// Normal verify correctly rejects it
try {
jwt.verify(expiredToken, SECRET);
} catch (e) {
console.log(e.message); // "jwt expired"
}
// Bypass with MAX_SAFE_INTEGER clockTolerance
const payload = jwt.verify(expiredToken, SECRET, {
clockTolerance: Number.MAX_SAFE_INTEGER // 9007199254740991
});
console.log(payload); // { sub: "user", role: "admin", ... } — token accepted!
Root Cause
In verify.js, the expiry check is:
if (clockTimestamp >= payload.exp + (options.clockTolerance || 0)) {
return done(new TokenExpiredError(...));
}
When clockTolerance is Number.MAX_SAFE_INTEGER, the addition payload.exp + 9007199254740991 produces a value far larger than any realistic clockTimestamp. The comparison becomes:
1775002429 >= 9007200998207420 → false
So the expiry check is skipped entirely. The same issue affects the nbf (not before) check via the same pattern.
No validation is performed on clockTolerance beyond checking it is a number:
// Current validation (insufficient):
if (options.clockTimestamp && typeof options.clockTimestamp !== "number") {
return done(new JsonWebTokenError("clockTimestamp must be a number"));
}
// clockTolerance has NO validation at all
Impact
Any application that:
- Reads
clockTolerancefrom user input, a config file, environment variable, or a database without strict validation, OR - Has a dependency that passes an unvalidated
clockTolerance
...is vulnerable to complete expiry bypass. An attacker who can influence the clockTolerance value can reuse tokens that expired days, months, or years ago.
This is particularly dangerous in multi-tenant systems where token verification options may be partially user-controlled.
Suggested Fix
Add an upper bound to clockTolerance. A reasonable maximum is 300 seconds (5 minutes) or at most 86400 (1 day). For example:
if (options.clockTolerance !== undefined) {
if (typeof options.clockTolerance !== "number" || options.clockTolerance < 0) {
return done(new JsonWebTokenError("clockTolerance must be a non-negative number"));
}
if (options.clockTolerance > 300) {
return done(new JsonWebTokenError("clockTolerance must not exceed 300 seconds"));
}
}
Alternatively, document clearly that clockTolerance must be a small value and add a warning when it exceeds a reasonable threshold.
Discovered via manual source code audit of v9.0.3.
Reported by Travis Burmaster — [email protected]
- Lingua principale
- JavaScript
- Stelle
- 18.2k
- Fork
- 1.3k
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi 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" errorsForse già presa @cobyfrombrooklyn-bot l’ha presa 227 giorni fa. 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 ·
-
verify() resolves a string secret by attempting createPublicKey() first, costing 4x-52x on the HS* pathForse già presa @Hashim1999164 l’ha presa 22 giorni fa. Aperta
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
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
capricorn86/happy-dom#2485 ·
I maintainer di solito rispondono entro 2 giorni
-
area:space-accuracy good first issue track:data
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
Sara-Managed-Projects/space-radar#904 ·
I maintainer di solito rispondono entro 1 giorno