JWT Tokenrotatie vernieuwen: veilig patroon voor echte systemen

De meeste JWT-tutorials stoppen bij 'gebruik een vernieuwingstoken voor langdurige sessies'. Ze leggen zelden uit wat er gebeurt als dat vernieuwingstoken wordt gestolen. Een statisch vernieuwingstoken (een token dat nooit verandert) geeft een aanvaller dezelfde permanente toegang als een gestolen sessiecookie. Erger nog, er is geen manier om de diefstal te detecteren.
Vernieuwingstokenrotatie lost dit op door bij elk gebruik een nieuw vernieuwingstoken uit te geven. Als een oud token opnieuw wordt afgespeeld, wordt de hele sessie ingetrokken. In dit artikel wordt het implementatiepatroon besproken dat wordt gebruikt door Auth0, Okta en andere serieuze identiteitsproviders.
Waarom statische vernieuwingstokens mislukken
Een statisch vernieuwingstoken heeft een vaste waarde voor de gehele sessieduur (vaak meer dan 30 dagen). Problemen:
- Diefstal gebeurt stil: als een aanvaller het vernieuwingstoken steelt, kan deze voor onbepaalde tijd nieuwe toegangstokens genereren zonder dat de gebruiker het weet
- Geen herhalingsdetectie — hetzelfde token kan herhaaldelijk worden gebruikt vanaf verschillende locaties
- Intrekking is grof — om één gestolen token ongeldig te maken, moet je vaak alle gebruikerssessies ongeldig maken
De oplossing is om elk vernieuwingstoken te behandelen als eenmalig gebruik.
Het rotatiemodel
Stap 1: Inloggen — Maak een tokenfamilie
Wanneer de gebruiker zich authenticeert, genereert u een family_id (UUID) die alle tokens in deze sessie groepeert:
{ family_id: "f47ac10b-58cc-4372-a567-0e02b2c3d479", user_id: "user_123", refresh_token_hash: sha256(refresh_token), created_at: "2026-03-28T10:00:00Z", expires_at: "2026-04-27T10:00:00Z", used: false
}Sla deze record op de server op. Bewaar vernieuwingstokens nooit in localStorage. Gebruik httpOnly cookies of beveilig de eigen opslag.
Stap 2: Token vernieuwen — Roteren
Wanneer de client het vernieuwingstoken presenteert:
- Zoek de token-hash op in de database
- Controleer of het tot een actieve familie behoort en niet is gemarkeerd als
used - Markeer het als
gebruikt: waar - Geef een nieuw vernieuwingstoken uit (nieuwe willekeurige waarde, dezelfde
family_id) - Een nieuw toegangstoken uitgeven
- Sla de nieuwe vernieuwingstoken-hash op
Stap 3: Herhalingsdetectie
Als een vernieuwingstoken gemarkeerd als used: true opnieuw wordt weergegeven, betekent dit:
- Een aanvaller speelt een gestolen token opnieuw, of
- Een legitieme client probeert het opnieuw na een netwerkfout
Het veilige antwoord: alle tokens in de familie intrekken. Hierdoor wordt de gebruiker gedwongen opnieuw in te loggen. Het is beter om een legitieme gebruiker één keer lastig te vallen dan een aanvaller toegang te laten behouden.
// Herhaling gedetecteerd: nucleaire optie
wacht op db.query( 'VERWIJDEREN VAN refresh_tokens WAAR family_id = $1', [familie-ID]
);Databaseschema
Een minimale vernieuwingstokentabel:
TABEL MAKEN refresh_tokens ( id UUID PRIMAIRE SLEUTEL STANDAARD gen_random_uuid(), family_id UUID NIET NULL, user_id UUID NIET NULL REFERENTIES gebruikers(id), token_hash TEKST NIET NULL UNIEK, gebruikte BOOLEAN STANDAARD ONWAAR, aangemaakt_op TIMESTAMPTZ DEFAULT nu(), vervalt_om TIMESTAMPTZ NIET NULL, vervangen_door UUID REFERENTIES refresh_tokens(id)
); MAAK INDEX idx_refresh_family AAN refresh_tokens(family_id);
INDEX MAKEN idx_refresh_user AAN refresh_tokens(user_id);De kolom vervangen_door creëert een keten die u kunt volgen voor auditdoeleinden.
Gelijktijdige verzoeken afhandelen
In echte toepassingen kunnen meerdere API-aanroepen tegelijkertijd een vernieuwing activeren. Als oproep A het token roteert terwijl oproep B de oude nog vasthoudt, activeert oproep B herhalingsdetectie, waardoor de gebruiker onverwacht wordt uitgelogd.
Respijtperiodepatroon
Toestaan dat het vorige vernieuwingstoken geldig blijft gedurende een korte periode (5-10 seconden) na rotatie:
const GRACE_PERIOD_MS = 10_000; if (token.gebruikt) { const verstreken = Datum.nu() - token.rotated_at; als (verstreken < GRACE_PERIOD_MS) { // Within grace period — return the already-issued new tokens return getLatestTokensForFamily(token.family_id); } // Outside grace period — replay attack await revokeFamily(token.family_id); throw new UnauthorizedError('token_replayed');
}Monitoring en waarschuwingen
Volg deze signalen tijdens de productie:
- Gebeurtenissen opnieuw afspelen per uur — een piek duidt op een actieve aanval of een bug aan de clientzijde
- Familie-intrekkingen: hoge percentages kunnen duiden op tokendiefstal of een verkeerd geconfigureerde client
- Vernieuwingsfrequentie per gebruiker — abnormaal hoge frequenties duiden op het verzamelen van tokens
- Geografische afwijkingen — vernieuwen vanuit een ander land dan de oorspronkelijke login
Token vernieuwen versus toegangstoken: snelle referentie
| Eigenschap | Toegangstoken | Token vernieuwen |
|---|---|---|
| Levensduur | 5–15 minuten | 7–30 dagen |
| Formaat | JWT (op zichzelf staand) | Ondoorzichtige tekenreeks (aanbevolen) |
| Opslag | Geheugen (JS-variabele) | httpAlleen cookie |
| Verzonden naar | API-servers | Alleen authenticatieserver |
| Rotatie | Niet nodig | Elk gebruik |
Voor bugs in de validatie van tijdclaims die vaak interactie hebben met vernieuwingsstromen, zie JWT exp/iat/nbf veelvoorkomende bugs.
Veelgestelde vragen
Hoe moet ik gelijktijdige vernieuwingsverzoeken afhandelen?
Gebruik een korte respijtperiode (5-10 seconden) waarbij het vorige vernieuwingstoken nog steeds wordt geaccepteerd. Hiermee worden raceomstandigheden afgehandeld wanneer meerdere API-aanroepen tegelijkertijd een vernieuwing activeren. Na de respijtperiode is alleen het nieuwste token geldig.
Moeten vernieuwingstokens JWT of ondoorzichtige tekenreeksen zijn?
Ondoorzichtige tekenreeksen zijn over het algemeen veiliger voor vernieuwingstokens. JWT's bevatten payloadgegevens die het aanvalsoppervlak vergroten, en vernieuwingstokens hoeven niet op zichzelf te staan, aangezien de server ze toch altijd in de database opzoekt.
Hoe lang moeten vernieuwingssessies live zijn?
7–30 dagen is gebruikelijk. Hoogbeveiligde toepassingen (banken, gezondheidszorg) zouden 1 tot 7 dagen nodig hebben. Consumenten-apps kunnen bij rotatie maximaal 90 dagen meegaan. Combineer lange sessies altijd met apparaatvingerafdrukken en afwijkingsdetectie.
Gerelateerde tools en artikelen
- JWT Decoder — inspecteer tokens en verifieer tijdclaims
- JWT-beveiligingsfouten — veelvoorkomende JWT-valkuilen die verder gaan dan vernieuwingstokens
- JWT tijdclaimbugs — problemen met klokafwijking en validatievolgorde
- 2FA Gids — versterk de authenticatie die verder gaat dan alleen tokens