HS256 vs. RS256 ist die erste Entscheidung, wenn du JWTs ausstellst. Die beiden Werte für alg sehen ähnlich aus, aber HS256 authentifiziert Tokens mit einem gemeinsamen Geheimnis (HMAC-SHA256), während RS256 sie mit einem privaten RSA-Schlüssel signiert (RSASSA-PKCS1-v1_5 mit SHA-256). Davon hängt ab, wer Tokens erzeugen kann, wie Schlüssel verteilt werden und was passiert, wenn ein Schlüssel durchsickert.
Auf einen Blick
| HS256 | RS256 | |
|---|---|---|
| Algorithmus | HMAC mit SHA-256 (RFC 7518 §3.2) | RSASSA-PKCS1-v1_5 mit SHA-256 (§3.3) |
| Schlüssel | Ein gemeinsames Geheimnis, mindestens 256 Bit | RSA-Schlüsselpaar, mindestens 2048 Bit |
| Wer prüfen kann | Inhaber des Geheimnisses | Jeder mit dem öffentlichen Schlüssel |
| Wer Tokens ausstellen kann | Jeder Inhaber des Geheimnisses, auch Verifizierer | Nur der Inhaber des privaten Schlüssels |
| Schlüsselverteilung | Vertrauliche Kopie an jeden Verifizierer | Öffentliches JWKS (jwks_uri) |
| Signaturteil | 32 Byte, 43 Zeichen in base64url | 256 Byte, 342 Zeichen (RSA-2048) |
| Beispiel-Token (unten gemessen) | 187 Zeichen | 508 Zeichen |
| Geschwindigkeit, ein Kern eines Apple M4 Max | Millionen HMACs pro Sekunde | etwa 3.000 Signaturen und 114.000 Prüfungen pro Sekunde |
| Anforderungsstufe in JWA | Required | Recommended |
| Typischer Fehlerfall | Schwaches oder durchgesickertes Geheimnis: Jeder kann Tokens ausstellen | Algorithm Confusion bei nachlässigen Verifizierern; Umgang mit dem Schlüssel |
Das Vertrauensmodell
Bei HS256 sind Prüfschlüssel und Signaturschlüssel dieselben Bytes. Jeder Dienst, der Tokens prüft, kann auch gültige Tokens für jeden Benutzer, jeden Scope und jede Audience erzeugen, und ein Geheimnis, das aus einem einzigen dieser Dienste durchsickert (eine Umgebungsvariable in einem Log, ein Frontend-Bundle, eine mobile App), erlaubt jedem, Tokens auszustellen. Für einen Monolithen, der seine eigenen Session-Tokens ausstellt und prüft, ist das akzeptabel; wenn ein Token-Aussteller viele APIs oder Teams bedient, ist es eine ernste Schwäche.
Bei RS256 behält der Aussteller den privaten Schlüssel, idealerweise in einem HSM oder KMS, und veröffentlicht den öffentlichen Schlüssel in einem JWKS unter einer URL, die in seinen Metadaten steht. Verifizierer rufen den Schlüsselsatz ab, wählen Schlüssel anhand von kid aus und lassen sich hinzufügen oder entfernen, ohne dass Geheimnisse geteilt werden. Ein kompromittierter Verifizierer kann Tokens lesen, aber nicht fälschen. Deshalb signieren Anbieter von OpenID Connect die ID-Tokens standardmäßig mit RS256, und deshalb empfiehlt der RFC zu Best Practices für JWTs (RFC 8725) für Einsätze mit mehreren Parteien asymmetrische Algorithmen.
OpenID Connect erlaubt durchaus HS256-ID-Tokens für vertrauliche Clients, mit dem Client-Secret als Schlüssel; das funktioniert, weil jeder Client nur Tokens prüft, die für ihn selbst ausgestellt wurden.
Stärke des Geheimnisses und Offline-Knacken
Ein HS256-Token ist ein perfektes Orakel für Offline-Tests: Wer ein einziges Token sieht, kann Kandidaten für das Geheimnis so schnell durchprobieren, wie seine Hardware HMAC-SHA256 berechnet, ohne mit deinem Server zu kommunizieren. Kurze, von Menschen gewählte oder voreingestellte Geheimnisse („secret“, ein Projektname, das Beispiel aus einem Tutorial) werden mit gängigen Tools zum Knacken von Passwörtern in Sekunden ermittelt. RFC 7518 verlangt einen Schlüssel, der mindestens so lang ist wie die Ausgabe der Hashfunktion, also 256 Bit für HS256, und laut RFC 8725 §3.5 müssen Schlüssel genügend Entropie haben. Erzeuge das Geheimnis mit einem CSPRNG (openssl rand -base64 32) und leite es nie aus einem Passwort ab.
Bei RS256 gibt es keinen vergleichbaren Offline-Angriff auf den privaten Schlüssel, aber RFC 7518 lässt keine RSA-Schlüssel unter 2048 Bit zu, und die Schlüsseldatei muss wie jeder private Schlüssel geschützt werden.
Algorithm Confusion zwischen beiden
Die bekannteste JWT-Schwachstelle vermischt die beiden. Ein Dienst prüft RS256-Tokens, indem er eine Bibliothek mit dem öffentlichen RSA-Schlüssel im PEM-Format aufruft. Übernimmt die Bibliothek den Algorithmus aus dem Header des Tokens und unterstützt sie auch HS256, schickt ein Angreifer ein Token mit "alg": "HS256", dessen HMAC mit den PEM-Bytes des öffentlichen Schlüssels als Geheimnis berechnet wurde; die Bibliothek verwendet dieselben Bytes, und die Fälschung besteht die Prüfung. Tim McLean beschrieb das 2015 zusammen mit "alg": "none", und RFC 8725 §2.1 und §3.1 schreiben die Abhilfe fest: Der Verifizierer, nicht das Token, entscheidet, mit welchem Algorithmus jeder Schlüssel verwendet werden darf.
Moderne Bibliotheken machen das schwer: jsonwebtoken weigert sich, einen öffentlichen Schlüssel als HMAC-Geheimnis zu verwenden (der Fehler secretOrPublicKey must be an asymmetric key erscheint, wenn Konfigurationen die beiden verwechseln), PyJWT verlangt ein Argument algorithms, und jose bindet Algorithmen an Schlüsseltypen. Übergib trotzdem immer eine explizite Allowlist mit genau dem Algorithmus, den du erwartest.
Größe und Geschwindigkeit
Das folgende Skript, ausgeführt mit Node.js 26.10, baut aus denselben Claims nur mit node:crypto ein HS256- und ein RS256-Token.
import { createHmac, generateKeyPairSync, randomBytes, sign } from "node:crypto";
const b64url = (value) => Buffer.from(value).toString("base64url");
const payload = b64url(JSON.stringify({ iss: "https://auth.example.com", sub: "user-42", aud: "api", exp: 1792500000 }));
// HS256: HMAC-SHA256 with a shared 256-bit secret.
const secret = randomBytes(32);
const hsInput = `${b64url(JSON.stringify({ alg: "HS256", typ: "JWT" }))}.${payload}`;
const hs256 = `${hsInput}.${b64url(createHmac("sha256", secret).update(hsInput).digest())}`;
// RS256: RSASSA-PKCS1-v1_5 with SHA-256 and a 2048-bit key.
const { privateKey } = generateKeyPairSync("rsa", { modulusLength: 2048 });
const rsInput = `${b64url(JSON.stringify({ alg: "RS256", typ: "JWT", kid: "2026-10" }))}.${payload}`;
const rs256 = `${rsInput}.${b64url(sign("sha256", Buffer.from(rsInput), privateKey))}`;
console.log("HS256 token:", hs256.length, "characters, signature part:", hs256.split(".")[2].length);
console.log("RS256 token:", rs256.length, "characters, signature part:", rs256.split(".")[2].length);
HS256 token: 187 characters, signature part: 43
RS256 token: 508 characters, signature part: 342
Der RS256-Header enthält außerdem einen kid, den Verifizierer brauchen, um den Schlüssel aus einem JWKS auszuwählen. Verwende in Produktion eine gepflegte JWT-Bibliothek statt selbst gebauter Tokens; hier geht es um die Größe des Signaturteils. Einen Vergleich derselben Abwägung mit ECDSA findest du unter ES256 vs. RS256.
Wann welches Verfahren passt
- Eine einzelne Anwendung, die ihre eigenen Session-Tokens ausstellt und prüft: HS256 mit einem zufälligen 32-Byte-Geheimnis, rotiert mit kurzer Überlappung.
- Ein Autorisierungsserver, dessen Tokens von mehreren APIs geprüft werden: RS256, PS256 oder ES256 mit in einem JWKS veröffentlichten Schlüsseln.
- Tokens, die von Dritten, mobilen Apps oder Browsern geprüft werden: nur asymmetrisch; ein an einen Client ausgeliefertes Geheimnis ist öffentlich.
- ID-Tokens von OpenID Connect für vertrauliche Clients: Beides funktioniert; asymmetrisch ist der Standard und einfacher zu betreiben.
- Webhooks an einzelne Kunden: HMAC-artige Verfahren sind verbreitet, weil jeder Kunde sein eigenes Geheimnis hat.
- Schlüsselverwahrung nach Compliance-Vorgaben (HSM, KMS): asymmetrische Schlüssel, da Dienste prüfen können, ohne Zugriff auf das Schlüsselmaterial zum Signieren zu haben.
Hinweise zur Migration
Umstieg von HS256 auf RS256 oder ES256 ohne harten Stichtag:
- Erzeuge das Schlüsselpaar in einem KMS oder HSM und veröffentliche den öffentlichen Schlüssel in einem JWKS mit neuem
kidundalg. - Bring den Verifizierern beides bei: Wähle den Schlüssel anhand von
kidaus und binde jeden Schlüssel an genau einen Algorithmus (das gemeinsame Geheimnis an HS256, den neuen Schlüssel an RS256). Lass nie zu, dass ein einzelner Aufruf zur Prüfung für dasselbe Schlüsselmaterial beide Algorithmen akzeptiert. - Beginne, asymmetrische Tokens auszustellen, mit dem neuen
kid; vorhandene HS256-Tokens lassen sich weiter prüfen, bis sie ablaufen. - Entferne das gemeinsame Geheimnis von jedem Verifizierer, sobald die längste Laufzeit eines HS256-Tokens verstrichen ist. Dieser Schritt ist der eigentliche Sicherheitsgewinn.
- Rotiere das alte Geheimnis trotzdem, wenn es auch nur möglicherweise durchgesickert ist, denn ein Angreifer, der es besitzt, könnte weiterhin HS256-Tokens ausstellen, bis die Verifizierer sie nicht mehr akzeptieren.
Quellen
- RFC 7518, JSON Web Algorithms (§3.2 HMAC, §3.3 RSASSA-PKCS1-v1_5)
- RFC 8725, Best Current Practices für JWTs (§2.1, §3.1, §3.5)
- RFC 7519, JSON Web Token
- RFC 7517, JSON Web Key und JWK Set
- OpenID Connect Core 1.0 §10.1, Signieren mit MAC-Algorithmen
- Auth0: kritische Schwachstellen in Bibliotheken für JSON Web Tokens (2015)