TrendVidStream heeft zijn volledige authenticatiestack overgezet naar een rotating-refresh-token-systeem dat een gestolen token detecteert op het moment dat het opnieuw wordt gebruikt. De wijziging veranderde een 30-dagen durende JWT, die een aanvaller voorheen vrijelijk kon gebruiken, in een kortstondig credential dat onmiddellijk een lockout activeert.
Eén enkel datalek dwong tot deze overstap: een partner-SDK cachede een 30-dagen durende JWT in platte tekst, een aanvaller extraheerde deze en speelde hem opnieuw af vanuit een ander land, en de enige oplossing was het roteren van de signeringssleutel — een operatie die alle gebruikers uitlogde. Het incident heeft de tokenbeveiliging van het bedrijf opnieuw vormgegeven en vormt nu de basis voor de video-streamingdienst.
Waarom het oude model faalde
JWT's (JSON Web Tokens) zijn zelfstandige, ondertekende pakketjes waarmee een server een verzoek kan verifiëren zonder een database-lookup uit te voeren. Het gemak verbergt een prijs: als een token wekenlang geldig is, geeft het stelen ervan een aanvaller wekenlang toegang. Tijdens het datalek bleef het gestolen token geldig tot de vervaldatum na 30 dagen, omdat er geen manier was om het voortijdig ongeldig te maken.
Het roteren van de signeringssleutel is de enige wereldwijde manier om alle tokens ongeldig te maken, maar het dwingt elke gebruiker om opnieuw in te loggen, wat de dienstverlening verstoort en het vertrouwen schaadt. De fout lag niet in de JWT zelf, maar in het vertrouwen op een enkel, langdurig credential.
Het nieuwe ontwerp in een notendop
TrendVidStream geeft nu twee soorten tokens uit:
- Access tokens – geldig voor 15 minuten; ze bevatten de benodigde permissies voor elke API-aanroep.
- Refresh tokens – tokens die slechts eenmaal kunnen worden gebruikt om een kortstondig access token in te wisselen voor een nieuw paar.
Wanneer een client een refresh token presenteert, doet de server het volgende:
- Verifieert de handtekening en de claims van het token.
- Controleert of het token al eerder is gebruikt.
- Als de controle slaagt, wordt er een gloednieuw refresh token en een vers access token van 15 minuten uitgegeven.
- Markeert het oude refresh token als gebruikt.
Als stap 2 mislukt — wat betekent dat hetzelfde token voor de tweede keer verschijnt — behandelt de server dit als een signaal van diefstal en trekt hij de gehele "familie" van tokens in die aan die loginsessie is gekoppeld. Zowel het slachtoffer als de aanvaller wordt gedwongen om opnieuw in te loggen, waardoor de duur van het datalek wordt beperkt.
Het bijhouden van token-families
In plaats van elk token als een geïsoleerd record te behandelen, groepeert het systeem ze in families die beginnen wanneer een gebruiker inlogt. Elke rotatie creëert een nieuw lid van die familie. Het SQLite-schema dat in productie wordt gebruikt, slaat het volgende op:
- family_id – een stabiele identifier voor de gehele sessie.
- token_id – een unieke identifier voor elk refresh token.
- generation – een teller die nuttig is voor het debuggen.
- used_at – tijdstempel van wanneer het token is ingewisseld.
- revoked – een vlag die, wanneer ingesteld, de gehele familie uitschakelt.
Wanneer een hergebruik-event wordt gedetecteerd, wordt de revoked-vlag voor die family_id ingesteld, waardoor elk token dat bij de gecompromitteerde sessie hoort onmiddellijk ongeldig wordt gemaakt. Dit verandert een stille kaping in een melding met een hoog signaal die in de logs verschijnt.
Drie implementatieregels die het systeem betrouwbaar houden
Valideer eerst de handtekening Een aanvaller zou token-ID's kunnen raden en massale intrekkingen kan veroorzaken als de server controleert op "reeds gebruikt?" voordat de handtekening wordt geverifieerd. Door eerst de authenticiteit te bevestigen, worden onnodige denial-of-service-aanvallen voorkomen.
Sta een tolerantieperiode (grace window) toe Mobiele apps sturen vaak twee refresh-verzoeken kort achter elkaar wanneer een token verloopt. Als de server te streng is, wordt het tweede verzoek gemarkeerd als hergebruik, waardoor een legitieme gebruiker wordt uitgelogd. Een buffer van enkele seconden zorgt ervoor dat een "gebruikt" token nog steeds een nieuw paar kan retourneren, wat racecondities gladstrijkt.
Wikkel de volledige flow in een transactie met row locking Zonder atomiciteit zouden twee gelijktijdige verzoeken er allebei vanuit kunnen gaan dat zij de eerste zijn die een token gebruiken, waardoor er dubbele refresh tokens worden uitgegeven en de garantie van eenmalig gebruik wordt geschonden. Een database-transactie die de token-rij vergrendelt (locks), garandeert dat slechts één verzoek slaagt.
Conclusie: Door refresh tokens eenmalig te maken en te letten op hergebruik, kan een systeem elk gestolen credential veranderen in een alarm, waardoor gebruikers worden beschermd zonder een platformbrede uitlogbeurt te forceren.
