Een complete gids voor JWT-beveiliging: best practices voor validatie, opslag, algoritmes en het beperken van kwetsbaarheden in internationale applicaties.
JWT Tokens: Beste Praktijken voor Beveiliging in Wereldwijde Applicaties
JSON Web Tokens (JWT's) zijn een standaardmethode geworden om claims veilig uit te wisselen tussen twee partijen. Hun compacte structuur, gebruiksgemak en brede ondersteuning op diverse platformen hebben ze een populaire keuze gemaakt voor authenticatie en autorisatie in moderne webapplicaties, API's en microservices. Hun wijdverbreide adoptie heeft echter ook geleid tot meer kritisch onderzoek en de ontdekking van talrijke beveiligingskwetsbaarheden. Deze uitgebreide gids verkent de beste praktijken voor JWT-beveiliging om ervoor te zorgen dat uw wereldwijde applicaties veilig en veerkrachtig blijven tegen mogelijke aanvallen.
Wat zijn JWT's en hoe werken ze?
Een JWT is een op JSON gebaseerd beveiligingstoken dat uit drie delen bestaat:
- Header: Specificeert het type token (JWT) en het gebruikte ondertekeningsalgoritme (bijv. HMAC SHA256 of RSA).
- Payload: Bevat claims, dit zijn verklaringen over een entiteit (meestal de gebruiker) en aanvullende metadata. Claims kunnen geregistreerd zijn (bijv. issuer, subject, expiration time), publiek (gedefinieerd door de applicatie), of privaat (aangepaste claims).
- Signature: Wordt gecreëerd door de gecodeerde header, de gecodeerde payload, een geheime sleutel (voor HMAC-algoritmen) of een privésleutel (voor RSA/ECDSA-algoritmen) en het gespecificeerde algoritme te combineren en het resultaat te ondertekenen.
Deze drie delen worden Base64 URL-gecodeerd en samengevoegd met punten (.
) om de uiteindelijke JWT-string te vormen. Wanneer een gebruiker authenticeert, genereert de server een JWT, die de client vervolgens opslaat (meestal in local storage of een cookie) en meestuurt met volgende verzoeken. De server valideert vervolgens de JWT om het verzoek te autoriseren.
Veelvoorkomende JWT-kwetsbaarheden begrijpen
Voordat we dieper ingaan op de beste praktijken, is het cruciaal om de veelvoorkomende kwetsbaarheden van JWT's te begrijpen:
- Algoritmeverwarring (Algorithm Confusion): Aanvallers misbruiken de mogelijkheid om de
alg
-headerparameter te wijzigen van een sterk asymmetrisch algoritme (zoals RSA) naar een zwak symmetrisch algoritme (zoals HMAC). Als de server de publieke sleutel gebruikt als de geheime sleutel in het HMAC-algoritme, kunnen aanvallers JWT's vervalsen. - Blootstelling van geheime sleutel: Als de geheime sleutel die wordt gebruikt voor het ondertekenen van JWT's wordt gecompromitteerd, kunnen aanvallers geldige JWT's genereren en zich voordoen als elke gebruiker. Dit kan gebeuren door codelekken, onveilige opslag of kwetsbaarheden in andere delen van de applicatie.
- Token-diefstal (XSS/CSRF): Als JWT's onveilig worden opgeslagen, kunnen aanvallers ze stelen via Cross-Site Scripting (XSS) of Cross-Site Request Forgery (CSRF) aanvallen.
- Replay Attacks: Aanvallers kunnen geldige JWT's hergebruiken om ongeautoriseerde toegang te krijgen, vooral als de tokens een lange levensduur hebben en er geen specifieke tegenmaatregelen zijn geïmplementeerd.
- Padding Oracle Attacks: Wanneer JWT's worden versleuteld met bepaalde algoritmen en de opvulling (padding) onjuist wordt afgehandeld, kunnen aanvallers mogelijk de JWT ontsleutelen en de inhoud ervan benaderen.
- Problemen met klokverschil (Clock Skew): In gedistribueerde systemen kan een klokverschil tussen verschillende servers leiden tot mislukte JWT-validaties, met name bij verloopclaims.
Beste Praktijken voor JWT-beveiliging
Hier zijn uitgebreide beveiligingspraktijken om de risico's die aan JWT's verbonden zijn te beperken:
1. Het juiste ondertekeningsalgoritme kiezen
De keuze van het ondertekeningsalgoritme is cruciaal. Hier is waar u rekening mee moet houden:
- Vermijd
alg: none
: Sta nooit toe dat dealg
-header opnone
wordt ingesteld. Dit schakelt de handtekeningverificatie uit, waardoor iedereen geldige JWT's kan creëren. Veel bibliotheken zijn gepatcht om dit te voorkomen, maar zorg ervoor dat uw bibliotheken up-to-date zijn. - Geef de voorkeur aan asymmetrische algoritmen (RSA/ECDSA): Gebruik waar mogelijk RSA- (RS256, RS384, RS512) of ECDSA- (ES256, ES384, ES512) algoritmen. Asymmetrische algoritmen gebruiken een privésleutel voor ondertekening en een publieke sleutel voor verificatie. Dit voorkomt dat aanvallers tokens kunnen vervalsen, zelfs als ze toegang krijgen tot de publieke sleutel.
- Beheer privésleutels veilig: Sla privésleutels veilig op, met behulp van hardware security modules (HSM's) of veilige sleutelbeheersystemen. Commit nooit privésleutels naar broncode-repositories.
- Roteer sleutels regelmatig: Implementeer een strategie voor sleutelrotatie om de ondertekeningssleutels regelmatig te wijzigen. Dit minimaliseert de impact als een sleutel ooit wordt gecompromitteerd. Overweeg het gebruik van JSON Web Key Sets (JWKS) om uw publieke sleutels te publiceren.
Voorbeeld: JWKS gebruiken voor sleutelrotatie
Een JWKS-eindpunt biedt een set publieke sleutels die kunnen worden gebruikt om JWT's te verifiëren. De server kan sleutels roteren en clients kunnen automatisch hun sleutelset bijwerken door het JWKS-eindpunt op te halen.
/.well-known/jwks.json
:
{
"keys": [
{
"kty": "RSA",
"kid": "key1",
"alg": "RS256",
"n": "...",
"e": "AQAB"
},
{
"kty": "RSA",
"kid": "key2",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}
2. JWT's correct valideren
Een juiste validatie is essentieel om aanvallen te voorkomen:
- Verifieer de handtekening: Verifieer altijd de JWT-handtekening met de juiste sleutel en het juiste algoritme. Zorg ervoor dat uw JWT-bibliotheek correct is geconfigureerd en up-to-date is.
- Valideer de claims: Valideer essentiële claims zoals
exp
(verlooptijd),nbf
(niet voor),iss
(uitgever) enaud
(doelgroep). - Controleer de
exp
-claim: Zorg ervoor dat de JWT niet is verlopen. Implementeer een redelijke levensduur voor tokens om de kans voor aanvallers te minimaliseren. - Controleer de
nbf
-claim: Zorg ervoor dat de JWT niet wordt gebruikt vóór de geldige starttijd. Dit voorkomt replay attacks voordat het token bedoeld is om te worden gebruikt. - Controleer de
iss
-claim: Verifieer dat de JWT is uitgegeven door een vertrouwde uitgever. Dit voorkomt dat aanvallers JWT's gebruiken die door onbevoegde partijen zijn uitgegeven. - Controleer de
aud
-claim: Verifieer dat de JWT bedoeld is voor uw applicatie. Dit voorkomt dat JWT's die voor andere applicaties zijn uitgegeven, tegen die van u worden gebruikt. - Implementeer een weigerlijst (optioneel): Voor kritieke applicaties, overweeg het implementeren van een weigerlijst (ook bekend als een intrekkingslijst) om gecompromitteerde JWT's ongeldig te maken vóór hun verlooptijd. Dit voegt complexiteit toe, maar kan de beveiliging aanzienlijk verbeteren.
Voorbeeld: Claims valideren in code (Node.js met jsonwebtoken
)
const jwt = require('jsonwebtoken');
try {
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'https://example.com',
audience: 'https://myapp.com'
});
console.log(decoded);
} catch (error) {
console.error('JWT-validatie mislukt:', error);
}
3. JWT's veilig opslaan aan de client-zijde
Hoe JWT's aan de client-zijde worden opgeslagen, heeft een aanzienlijke invloed op de beveiliging:
- Vermijd Local Storage: Het opslaan van JWT's in local storage maakt ze kwetsbaar voor XSS-aanvallen. Als een aanvaller JavaScript in uw applicatie kan injecteren, kan hij de JWT gemakkelijk stelen uit de local storage.
- Gebruik HTTP-Only Cookies: Sla JWT's op in HTTP-only cookies met de attributen
Secure
enSameSite
. HTTP-only cookies zijn niet toegankelijk voor JavaScript, wat XSS-risico's beperkt. HetSecure
-attribuut zorgt ervoor dat de cookie alleen via HTTPS wordt verzonden. HetSameSite
-attribuut helpt CSRF-aanvallen te voorkomen. - Overweeg Refresh Tokens: Implementeer een refresh token-mechanisme. Toegangstokens met een korte levensduur worden gebruikt voor onmiddellijke autorisatie, terwijl refresh tokens met een lange levensduur worden gebruikt om nieuwe toegangstokens te verkrijgen. Sla refresh tokens veilig op (bijv. in een database met encryptie).
- Implementeer CSRF-bescherming: Wanneer u cookies gebruikt, implementeer dan CSRF-beschermingsmechanismen, zoals synchronizer tokens of het Double Submit Cookie-patroon.
Voorbeeld: HTTP-Only Cookies instellen (Node.js met Express)
app.get('/login', (req, res) => {
// ... authenticatielogica ...
const token = jwt.sign({ userId: user.id }, privateKey, { expiresIn: '15m' });
const refreshToken = jwt.sign({ userId: user.id }, refreshPrivateKey, { expiresIn: '7d' });
res.cookie('accessToken', token, {
httpOnly: true,
secure: true, // Zet op true in productieomgeving
sameSite: 'strict', // of 'lax' afhankelijk van uw behoeften
maxAge: 15 * 60 * 1000 // 15 minuten
});
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true, // Zet op true in productieomgeving
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 dagen
});
res.send({ message: 'Inloggen geslaagd' });
});
4. Bescherming tegen 'Algorithm Confusion'-aanvallen
Algoritmeverwarring is een kritieke kwetsbaarheid. Zo kunt u het voorkomen:
- Specificeer expliciet toegestane algoritmen: Specificeer bij het verifiëren van JWT's expliciet de toegestane ondertekeningsalgoritmen. Vertrouw niet op de JWT-bibliotheek om het algoritme automatisch te bepalen.
- Vertrouw de
alg
-header niet: Vertrouw nooit blindelings dealg
-header in de JWT. Valideer deze altijd tegen een vooraf gedefinieerde lijst van toegestane algoritmen. - Gebruik sterke statische typering (indien mogelijk): In talen die statische typering ondersteunen, dwing een strikte typecontrole af voor de sleutel- en algoritme-parameters.
Voorbeeld: 'Algorithm Confusion' voorkomen (Node.js met jsonwebtoken
)
const jwt = require('jsonwebtoken');
try {
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256'] // Sta expliciet alleen RS256 toe
});
console.log(decoded);
} catch (error) {
console.error('JWT-validatie mislukt:', error);
}
5. Correcte mechanismen voor tokenverloop en -vernieuwing implementeren
De levensduur van een token is een belangrijke beveiligingsoverweging:
- Gebruik toegangstokens met een korte levensduur: Houd de levensduur van toegangstokens kort (bijv. 5-30 minuten). Dit beperkt de impact als een token wordt gecompromitteerd.
- Implementeer Refresh Tokens: Gebruik refresh tokens om nieuwe toegangstokens te verkrijgen zonder dat de gebruiker opnieuw hoeft te authenticeren. Refresh tokens kunnen een langere levensduur hebben, maar moeten veilig worden opgeslagen.
- Implementeer Refresh Token-rotatie: Roteer refresh tokens telkens wanneer een nieuw toegangstoken wordt uitgegeven. Dit maakt het oude refresh token ongeldig, waardoor de potentiële schade wordt beperkt als een refresh token wordt gecompromitteerd.
- Overweeg sessiebeheer: Voor gevoelige applicaties, overweeg om naast JWT's ook server-side sessiebeheer te implementeren. Hiermee kunt u de toegang meer granulair intrekken.
6. Bescherming tegen token-diefstal
Het voorkomen van token-diefstal is cruciaal:
- Implementeer een strikt Content Security Policy (CSP): Gebruik CSP om XSS-aanvallen te voorkomen. Met CSP kunt u specificeren welke bronnen toestemming hebben om resources (scripts, stijlen, afbeeldingen, etc.) op uw website te laden.
- Sanitize gebruikersinvoer: Sanitize alle gebruikersinvoer om XSS-aanvallen te voorkomen. Gebruik een vertrouwde HTML-sanitizer-bibliotheek om potentieel kwaadaardige tekens te escapen.
- Gebruik HTTPS: Gebruik altijd HTTPS om de communicatie tussen de client en de server te versleutelen. Dit voorkomt dat aanvallers netwerkverkeer afluisteren en JWT's stelen.
- Implementeer HSTS (HTTP Strict Transport Security): Gebruik HSTS om browsers te instrueren altijd HTTPS te gebruiken bij de communicatie met uw website.
7. Monitoring en logging
Effectieve monitoring en logging zijn essentieel voor het detecteren van en reageren op beveiligingsincidenten:
- Log JWT-uitgifte en -validatie: Log alle gebeurtenissen met betrekking tot JWT-uitgifte en -validatie, inclusief de gebruikers-ID, het IP-adres en de tijdstempel.
- Monitor op verdachte activiteit: Monitor op ongebruikelijke patronen, zoals meerdere mislukte inlogpogingen, JWT's die tegelijkertijd vanaf verschillende locaties worden gebruikt, of snelle tokenvernieuwingsverzoeken.
- Stel waarschuwingen in: Stel waarschuwingen in om u op de hoogte te stellen van mogelijke beveiligingsincidenten.
- Controleer logs regelmatig: Controleer de logs regelmatig om verdachte activiteiten te identificeren en te onderzoeken.
8. Rate Limiting
Implementeer rate limiting om brute-force- en denial-of-service (DoS)-aanvallen te voorkomen:
- Beperk inlogpogingen: Beperk het aantal mislukte inlogpogingen vanaf één IP-adres of gebruikersaccount.
- Beperk tokenvernieuwingsverzoeken: Beperk het aantal tokenvernieuwingsverzoeken vanaf één IP-adres of gebruikersaccount.
- Beperk API-verzoeken: Beperk het aantal API-verzoeken vanaf één IP-adres of gebruikersaccount.
9. Up-to-date blijven
- Houd bibliotheken bijgewerkt: Werk uw JWT-bibliotheken en afhankelijkheden regelmatig bij om beveiligingskwetsbaarheden te patchen.
- Volg beveiligingspraktijken: Blijf op de hoogte van de nieuwste beveiligingspraktijken en kwetsbaarheden met betrekking tot JWT's.
- Voer beveiligingsaudits uit: Voer regelmatig beveiligingsaudits van uw applicatie uit om potentiële kwetsbaarheden te identificeren en aan te pakken.
Wereldwijde overwegingen voor JWT-beveiliging
Bij het implementeren van JWT's voor wereldwijde applicaties, houd rekening met het volgende:
- Tijdzones: Zorg ervoor dat uw servers zijn gesynchroniseerd met een betrouwbare tijdbron (bijv. NTP) om problemen met klokverschillen te voorkomen die de JWT-validatie kunnen beïnvloeden, met name de
exp
- ennbf
-claims. Overweeg om consequent UTC-tijdstempels te gebruiken. - Regelgeving voor gegevensprivacy: Wees u bewust van de regelgeving voor gegevensprivacy, zoals GDPR, CCPA en andere. Minimaliseer de hoeveelheid persoonlijke gegevens die in JWT's wordt opgeslagen en zorg voor naleving van de relevante regelgeving. Versleutel gevoelige claims indien nodig.
- Internationalisatie (i18n): Wanneer u informatie uit JWT-claims weergeeft, zorg er dan voor dat de gegevens correct worden gelokaliseerd voor de taal en regio van de gebruiker. Dit omvat het correct formatteren van datums, getallen en valuta.
- Wettelijke naleving: Wees u bewust van eventuele wettelijke vereisten met betrekking tot gegevensopslag en -overdracht in verschillende landen. Zorg ervoor dat uw JWT-implementatie voldoet aan alle toepasselijke wet- en regelgeving.
- Cross-Origin Resource Sharing (CORS): Configureer CORS correct zodat uw applicatie toegang heeft tot bronnen van verschillende domeinen. Dit is met name belangrijk bij het gebruik van JWT's voor authenticatie tussen verschillende services of applicaties.
Conclusie
JWT's bieden een handige en efficiënte manier om authenticatie en autorisatie af te handelen, maar ze introduceren ook potentiële beveiligingsrisico's. Door deze beste praktijken te volgen, kunt u het risico op kwetsbaarheden aanzienlijk verminderen en de beveiliging van uw wereldwijde applicaties waarborgen. Blijf op de hoogte van de nieuwste beveiligingsdreigingen en werk uw implementatie dienovereenkomstig bij. Het prioriteren van beveiliging gedurende de gehele levenscyclus van JWT's helpt uw gebruikers en gegevens te beschermen tegen ongeautoriseerde toegang.