Webtokens, også kendt som JSON Web Tokens (JWT), er en metode til at autentificere og autorisere brugere i et webmiljø. De bruges til at sikre kommunikationen mellem en klient (f.eks. en webbrowser) og en server.
En webtoken består af tre dele: en header, en payload og en signatur. Headeren indeholder information om typen af token og den anvendte krypteringsalgoritme. Payloaden indeholder brugeroplysninger eller andre data relateret til autentifikation og autorisation. Signaturen bruges til at verificere tokenets integritet og gyldighed.
Webtokens er nyttige til at sikre webapplikationer, da de tillader klienten at gemme og sende autentificeringsoplysninger i form af en token. Dette eliminerer behovet for at gemme følsomme oplysninger som adgangskoder på klienten eller i cookies. Ved at bruge webtokens kan serveren validere og verificere klientens identitet ved hjælp af den gemte token.
Det er vigtigt at bemærke, at webtokens ikke er krypterede og derfor kan læses af enhver, der har adgang til dem. Derfor skal følsomme oplysninger undgås i tokenets payload. For at sikre fortrolighed kan man anvende kryptering sammen med webtokens.
Ved at implementere webtokens i en webapplikation kan man opnå en mere sikker og skalerbar autentifikations- og autorisationsløsning.
Man kan læse meget mere om det her!
JWT - JSON Web Token explained in 4 minutes (With Visuals)
Vores standart måde at implementere en JWT er at vi har en private nøgle som vi bruger til at hashe signaturen på vores Token. Det betyder at vi som har den private nøgle kan godkende om den faktisk er kommet fra os eller om nogen har ændret på vores token!
En af fordelene ved JWT med dobbelt nøgle (asymmetrisk kryptering) er, at den tilbyder bedre sikkerhed sammenlignet med symmetrisk kryptering (hvor samme nøgle bruges til både signering og verificering). Her er årsagerne til, at vi bør implementere JWT med dobbelt nøgle:
I .NET kan vi bruge RSA-nøgler til at signere og verificere JWT tokens. Den private nøgle holdes sikker på udstederserveren, mens den offentlige nøgle kan deles med andre tjenester der har behov for at verificere tokens.
Her er et komplet eksempel på, hvordan vi kan implementere JWT med asymmetrisk kryptering i .NET:
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
public class AsymmetricJwtService
{
private readonly RSA _privateKey;
private readonly RSA _publicKey;
public AsymmetricJwtService()
{
// Genererer nøglepar (i produktion vil du typisk indlæse nøgler fra sikker opbevaring)
_privateKey = RSA.Create(2048);
// Kopier den offentlige nøgle (vil typisk distribueres til andre tjenester)
_publicKey = RSA.Create();
_publicKey.ImportRSAPublicKey(_privateKey.ExportRSAPublicKey(), out _);
}
public string GenerateToken(string userId, string[] roles)
{
var handler = new JwtSecurityTokenHandler();
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, userId),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
// Tilføj roller som claimsforeach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
var credentials = new SigningCredentials(
new RsaSecurityKey(_privateKey),
SecurityAlgorithms.RsaSha256);
var token = new JwtSecurityToken(
issuer: "myissuer.dk",
audience: "myaudience.dk",
claims: claims,
expires: DateTime.UtcNow.AddHours(1),
signingCredentials: credentials
);
return handler.WriteToken(token);
}
public ClaimsPrincipal ValidateToken(string token)
{
var handler = new JwtSecurityTokenHandler();
var parameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new RsaSecurityKey(_publicKey),
ValidateIssuer = true,
ValidIssuer = "myissuer.dk",
ValidateAudience = true,
ValidAudience = "myaudience.dk",
ClockSkew = TimeSpan.Zero
};
return handler.ValidateToken(token, parameters, out _);
}
}
Ved implementering af JWT med dobbelt nøgle, bør vi tage hensyn til følgende sikkerhedsaspekter:
For at teste JWT-verifikation kan vi bruge CURL. Først får vi et token fra vores auth-service:
curl -X POST <https://myauthservice.dk/token> \\
-H "Content-Type: application/json" \\
-d '{"username":"brugernavn","password":"kodeord"}'
Derefter kan vi bruge dette token til at kalde en beskyttet tjeneste:
curl -X GET <https://myprotectedservice.dk/resource> \\
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
I en mikroservicearkitektur er JWT med dobbelt nøgle særligt fordelagtigt:
Ved at bruge JWT med dobbelt nøgle opnår vi både forbedret sikkerhed og bedre arkitekturmæssige fordele i distribuerede systemer.
Selvom dobbelt nøgle (asymmetrisk kryptering) giver mange fordele, har denne metode også nogle ulemper sammenlignet med symmetrisk kryptering:
For mindre applikationer eller systemer med færre komponenter kan den ekstra kompleksitet ved asymmetrisk kryptering overskygge fordelene. I disse tilfælde kan symmetrisk kryptering (som HMAC-SHA256) være tilstrækkelig og lettere at implementere.
Valget mellem symmetrisk og asymmetrisk kryptering bør baseres på systemets kompleksitet, sikkerhedskrav og arkitektur. For mikroservice-arkitekturer og distribuerede systemer er asymmetrisk kryptering ofte den bedste løsning på trods af disse ulemper.