Zruck zum Blog, du Zniachtl
März 2026·4 Min. zum LesnVo da KI gmocht

A praktischer Leitfodn zur JWT-Authentifizierung

#security#backend#authentication

JSON Web Tokens wern in vüle neichn Backends standardmäßig gewählt, oft ohne dass wer innehoit und prüft, ob a Token wirklich die richtige Wahl is, oda wos an dem Tag passiert, wo du oans widerrufn muasst. Do is, wos a JWT tatsächlich is, wia ma ihn richtig nutzt, und wo er wirklich ned passt.

Wos tatsächlich in an JWT drin is

A JWT besteht aus drei base64url-codierten Segmenten, mit Punkten verbunden: header.payload.signature. Nur die Signatur hängt von am Secret ab - Header und Payload sand nur codiert, ned verschlüsselt, und jeder kann sie decodieren und lesn.

// header
{ "alg": "HS256", "typ": "JWT" }

// payload (the "claims")
{ "sub": "user_4821", "role": "admin", "exp": 1774300800 }

// signature = HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)

Der letzte Punkt gehört wiederholt, weil er Leit dauernd aufs Glatteis führt: Pack nie wos Geheimes in die Payload. A JWT beweist, dass die Payload seit dem Signiern ned manipuliert wurde - er versteckt die Payload ned vor dem Inhaber vom Token oda vor irgendwem, dem er gezeigt wird.

A Token ausstelln

import jwt from 'jsonwebtoken';

function issueToken(user) {
  return jwt.sign(
    {
      sub: user.id,
      role: user.role,
    },
    process.env.JWT_SECRET,
    { expiresIn: '15m' },
  );
}

expiresIn setzt den exp-Claim - das Token wird strukturell ungültig zum Verifiziern, sobois die Zeit abglaufen is, ohne dass da Server irgendwos tracken muass. Diese Zustandslosigkeit is der ganze Reiz von JWTs: Jeder Server mit dem geteilten Secret (oda öffentlichen Schlüssel, bei asymmetrischer Signatur) kann a Token verifiziern, ohne Datenbankabfrage oda Aufruf an an Session-Store.

A Token verifiziern

function requireAuth(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'missing_token' });
  }

  try {
    const payload = jwt.verify(authHeader.slice(7), process.env.JWT_SECRET);
    req.user = { id: payload.sub, role: payload.role };
    next();
  } catch (err) {
    return res.status(401).json({ error: 'invalid_token' });
  }
}

jwt.verify prüft die Signatur und den Ablauf in oam Aufruf und wirft, wenn eins von beiden scheitert. Lass des imma die Bibliothek mochn - Signaturvergleich händisch zu schreibn is a leichter Weg, an Timing-Attack- oda Type-Confusion-Bug einzubauen (a bekannte, reale JWT-Schwachstellenklasse betraf Server, de dem alg-Feld im Token söba vertraut hom, wodurch a Angreifer auf alg: none umschalten und die Verifizierung komplett überspringa konnt; moderne Bibliotheken verlangen, dass du akzeptierte Algorithmen explizit angibst, statt der Behauptung vom Token über seinen eigenen Algorithmus zu trauen).

Des Widerrufs-Problem

Zustandslosigkeit is der Verkaufsschlager von JWT, und gleichzeitig seine schärfste Kante: Wennst nie wos gegn a Datenbank prüfst, kannst a ka bestimmtes Token vor Ablauf ungültig machen. A Nutzer ändert sei Passwort, loggt si aus oda wird gesperrt - und sein bestehendes Token funktioniert weiter bis exp, weil nix an "verifizier die Signatur" die Frage "soit des Token no akzeptiert wern?" enthoit.

Drei übliche Wege, damit umzugehn, in aufsteigender Reihenfolge, wie vül Zustandslosigkeit du aufgibst:

  1. Kurze Gültigkeit + Refresh-Tokens. Stöll a kurzlebiges Access-Token (Minuten) und a langlebigeres, datenbankgetracktes Refresh-Token aus. Zugriff zu widerrufn heißt, den Refresh-Token-Eintrag zu löschn; das Access-Token söba funktioniert noch bis zum natürlichen Ablauf, aber dieses Fenster is klan.
// on login
const accessToken = jwt.sign({ sub: user.id }, secret, { expiresIn: '15m' });
const refreshToken = crypto.randomUUID();
await db.refreshTokens.insert({ token: refreshToken, userId: user.id, expiresAt: /* 30 days */ });

// on refresh
async function refresh(refreshToken) {
  const record = await db.refreshTokens.findValid(refreshToken);
  if (!record) throw new Error('invalid_refresh_token');
  return jwt.sign({ sub: record.userId }, secret, { expiresIn: '15m' });
}
  1. A Denylist für den seltenen, dringenden Foi. Hoit an klanen, schnell abfragbaren Store (Redis, mit TTL passend zur Restlebensdauer vom Token) mit Tokens, de sofort abglehnt wern miassn - a gestohlenes Token, a Notfall-Sperre -, bei jedem Request zusätzlich zur Signaturprüfung gecheckt. Des bringt wieda an Lookup auf den Hot Path, aber nur für den Ausnahmefall, ned für jedes jemois ausgstellte Token.
  1. Nimm einfach Sessions. Wennst merkst, dass du a Denylist baust, de die meisten Tokens die meiste Zeit abdeckt, host server-seitige Sessions neu erfunden, nur mit zusätzlichen Schritten. Wenn sofortiger Widerruf wichtig is und dein Traffic JWTs service-übergreifende Zustandslosigkeit ned braucht, is a klassische Session-ID, gestützt von ana Datenbank oda Redis, einfacher und macht das eine, was JWTs strukturell schlecht können.

Wo di die Speicherung beißt

Wenn das Access-Token in localStorage lebt, ist es für jedes JavaScript lesbar, des auf da Seitn läuft - a für a eingschleustes Skript aus ana XSS-Lücke irgendwo sonst in da App. A httpOnly-Cookie kann von JavaScript gor ned glesn wern, wos genau diese Lücke schließt, aber dafür CSRF aufmacht, weil Browser Cookies automatisch an Requests hängen, egal welche Seitn sie ausglöst hot.

res.cookie('access_token', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
});

sameSite: 'strict' (oda mindestens 'lax') is das, wos CSRF hier tatsächlich entschärft - es hindert den Browser von vornherein dran, das Cookie an Requests zu hängen, de von ana ondern Seitn kemman.

Wann JWT des falsche Werkzeug is

  • A einzelner Monolith, der bei jedem Request eh mit seiner eigenen Datenbank redt. Du zoilst JWTs Komplexitätskosten (Widerrufsstrategie, Schlüsselverwaltung für die Signatur) für a Zustandslosigkeits-Vorteil, den du ned nutzt, weil a Session-Lookup die Datenbank getroffen hätt, die du eh scho triffst.
  • Olles, wos als harte Anforderung an Nutzer überoi, sofort, ausloggen muass - Passwort-Reset, Account-Kompromittierung. Plan von Tag oans a Denylist oda Refresh-Token-Widerruf ein, statt die Lücke während ana Störung zu entdecken.

Fazit

A JWT is a signierte, ned verschlüsselte Aussage über a Identität, de a Server ohne Datenbank-Roundtrip verifiziern kann - wirklich nützlich über Service-Grenzen hinweg, wo a geteilter Session-Store unhandlich wär. Der Kompromiss is, dass Widerruf ned gratis is: Entscheid vorab, ob du kurzlebige Tokens mit Refresh, a Denylist für Notfälle brauchst, oda ob du JWTs Zustandslosigkeit eigentlich gor ned brauchst.

NeicherDes CAP-Theorem, erklärt mit echten BeispielenElterEvent Sourcing vs. klassisches CRUD