In einem Satz
HMAC ist ein kryptografischer Handschlag, der einen gemeinsamen geheimen Schlüssel nutzt, um zu beweisen, dass eine Nachricht authentisch ist und niemand daran herumgepfuscht hat.
Welches Problem es löst
In den frühen, wilden Wildwest-Zeiten des Internets war das Senden einer Nachricht wie das Senden einer Postkarte. Jeder, der sie abfing, konnte sie lesen und vielleicht sogar darauf herumkritzeln, bevor er sie weiterschickte. Wenn du eine Postkarte mit der Aufschrift „Triff mich um Mitternacht, bring die Kohle mit“ bekommen hast, wie konntest du sicher sein, dass sie wirklich von deinem Geheimagenten-Kontakt kam und nicht von seiner Erzfeindin, der bösen Eva? Und wie konntest du sicher sein, dass nicht ursprünglich „Triff mich am Mittag zu einem freundlichen Mittagessen“ darauf stand?
Das ist das Doppelproblem von Authentizität (Ist es wirklich von dir?) und Integrität (Wurde es verändert?).
Eine einfache kryptografische Hash-Funktion (wie SHA-256) scheint ein guter erster Schritt zu sein. Du könntest deine Nachricht hashen, die Nachricht und den Hash senden, und der Empfänger könnte die Nachricht erneut hashen, um zu sehen, ob sie übereinstimmen. Großartig! Das löst das Integritätsproblem. Wenn ein einziges Byte der Nachricht geändert wurde, stimmen die Hashes nicht überein.
Aber es löst nicht das Authentizitätsproblem. Die böse Eva kann einfach die Nachricht ändern, einen neuen Hash für ihre neue Nachricht berechnen und beides weitersenden. Der Empfänger wird sehen, dass der Hash zur Nachricht passt, hat aber keine Möglichkeit zu wissen, dass das ganze Paket eine Fälschung ist.
Hier kommt HMAC (Hash-based Message Authentication Code) ins Spiel. Durch die Einführung eines gemeinsamen geheimen Schlüssels in den Hashing-Prozess erzeugt HMAC eine Signatur, die nur jemand erstellen kann, der diesen geheimen Schlüssel besitzt. Das ist der Unterschied zwischen einem einfachen Wachssiegel (jeder kann eines herstellen) und einem Wachssiegel, das mit einem einzigartigen Siegelring gemacht wurde (nur der König hat einen). HMAC gibt uns sowohl Integrität als auch Authentizität.
Wie es unter der Haube funktioniert
HMAC ist keine neue Art von Hash-Funktion; es ist ein Rezept, das bestehende Hash-Funktionen (wie SHA-256) auf eine clevere, spezifische Weise verwendet. Die offizielle Spezifikation ist RFC 2104, aber lassen wir es uns auf gut Deutsch aufschlüsseln.
Die Zutaten
Um einen HMAC zu brauen, brauchst du drei Dinge:
- Die Nachricht: Die Daten, die du schützen möchtest. Das könnte ein JSON-Payload für einen Webhook sein, eine Zeichenkette mit URL-Parametern oder ein beliebiger Brocken an Binärdaten.
- Der geheime Schlüssel: Eine Byte-Zeichenfolge, die nur dem Sender und dem Empfänger bekannt ist. Das ist die magische Zutat. Wenn er kompromittiert wird, ist das gesamte System kaputt.
- Die Hash-Funktion: Ein Standardalgorithmus wie SHA-1, SHA-256 oder SHA-512. Die Wahl der Hash-Funktion bestimmt die Länge der endgültigen HMAC-Signatur (z. B. erzeugt HMAC-SHA256 eine 256-Bit-Signatur).
Das Rezept (Der HMAC-Algorithmus)
Man könnte meinen, man könne einfach hash(key + message) machen. Das scheint einfach, aber diese Konstruktion ist anfällig für einige clevere Krypto-Tricksereien, die als „Length-Extension-Angriffe“ bezeichnet werden. Die offizielle HMAC-Konstruktion ist etwas komplizierter, speziell um diese Angriffe zu verhindern. Sie verwendet einen doppelten Hashing-Prozess.
Hier ist ein vereinfachter Blick auf die Schritte:
Den Schlüssel vorbereiten: Die Hash-Funktion arbeitet mit Blöcken fester Größe (z. B. 64 Bytes für SHA-256). Der Schlüssel muss so vorbereitet werden, dass er zu dieser Blockgröße passt.
- Wenn der Schlüssel länger als die Blockgröße ist, hasst du den Schlüssel selbst und verwendest dieses Ergebnis als neuen Schlüssel.
- Wenn der Schlüssel kürzer als die Blockgröße ist, füllst du ihn mit Null-Bytes auf, bis er die Blockgröße erreicht.
Innere und äußere Schlüssel erstellen: Aus diesem vorbereiteten Schlüssel leiten wir zwei separate Schlüssel ab.
ipad(inneres Pad): ein konstantes Byte (0x36), das wiederholt wird, um die Blockgröße zu füllen.opad(äußeres Pad): ein anderes konstantes Byte (0x5C), das wiederholt wird, um die Blockgröße zu füllen.
Wir erstellen einen
inner_padded_key, indem wir unseren vorbereiteten Schlüssel nehmen und ihn mitipadXOR-verknüpfen. Wir erstellen einenouter_padded_key, indem wir den vorbereiteten Schlüssel mitopadXOR-verknüpfen.Das doppelte Hashing durchführen: Jetzt zum Hauptereignis.
- Innerer Hash: Verkette den
inner_padded_keymit der ursprünglichen Nachricht und jage ihn durch die Hash-Funktion. - Äußerer Hash: Verkette den
outer_padded_keymit dem Ergebnis des inneren Hashes und jage das Ganze durch die Hash-Funktion.
- Innerer Hash: Verkette den
Das Endergebnis dieses äußeren Hashes ist deine HMAC-Signatur!
In Pseudocode sieht es so aus:
function hmac(key, message, hash_function, block_size) {
// 1. Prepare the key
if (key.length > block_size) {
key = hash_function(key);
}
if (key.length < block_size) {
key = pad_with_zeros(key, block_size);
}
// 2. Create inner and outer padded keys
o_key_pad = key XOR (0x5C repeated to block_size);
i_key_pad = key XOR (0x36 repeated to block_size);
// 3. Perform the double hash
inner_hash_result = hash_function(i_key_pad + message);
final_hmac = hash_function(o_key_pad + inner_hash_result);
return final_hmac;
}
Warum das doppelte Hashing?
Diese Innen-dann-Außen-Struktur ist das Geheimrezept. Der innere Hash kombiniert den Schlüssel und die Nachricht. Der äußere Hash hasht dann im Wesentlichen das Ergebnis der ersten Operation erneut mit dem Schlüssel. Das „versiegelt“ den inneren Hash. Es macht es für einen Angreifer rechnerisch unmöglich, das Zwischenergebnis des Hashes ohne Kenntnis des Schlüssels zu manipulieren, und wehrt so Length-Extension-Angriffe und andere potenzielle kryptografische Brüche ab. Es ist eine bewährte, robuste Konstruktion, die sich über die Zeit bewährt hat.
Geschichten aus der Praxis
Der GitHub-Webhook-Wächter
Ein Startup-Team hatte seinen Continuous-Integration-Server (CI-Server) so eingerichtet, dass ihre App jedes Mal automatisch in der Produktion bereitgestellt wurde, wenn jemand in den main-Branch pushte. Der Auslöser war ein Webhook von GitHub: eine POST-Anfrage von den GitHub-Servern an eine öffentliche URL auf ihrem CI-Server. Eines Nachts fand ihr streichlustiger Praktikant die öffentliche URL und begann mit einem einfachen cURL-Befehl, gefälschte Webhook-Payloads zu senden, was Dutzende nutzlose, ressourcenfressende Deployments auslöste.
Die Senior-Entwicklerin behob das Problem in 15 Minuten. In den Webhook-Einstellungen von GitHub erzeugte sie ein langes, zufälliges „Secret“. Sie kopierte dieses Secret und konfigurierte es als Umgebungsvariable auf ihrem CI-Server. GitHub verwendete dieses Secret nun, um für jeden Webhook-Payload eine HMAC-SHA256-Signatur zu generieren und diese in einem X-Hub-Signature-256-Header mitzusenden. Der Code des CI-Servers wurde aktualisiert, um die gleiche HMAC-Berechnung auf dem rohen Request Body, den er empfing, mit demselben Secret durchzuführen. Wenn die berechnete Signatur mit der im Header übereinstimmte, wurde die Anfrage verarbeitet. Wenn nicht, wurde sie mit einem 403 Forbidden abgewiesen. Die Streiche hörten sofort auf.
Lektion: Sichere deine Webhooks immer mit einer HMAC-Signaturverifizierung. Vertraue keiner eingehenden Anfrage, bevor sie nicht authentifiziert wurde.
Die API-Keksdose sichern
Ein Entwickler baute einen Dienst, der ein einfaches, signiertes Cookie zur Authentifizierung verwendete. Wenn sich ein Benutzer anmeldete, stellte der Server ein Cookie mit seiner user_id und einem expiry-Zeitstempel aus. Um zu verhindern, dass Benutzer ihr Cookie bearbeiten, um ein anderer Benutzer zu werden (z. B. user_id=123 in user_id=1 ändern), fügte der Entwickler eine HMAC-Signatur hinzu.
Der Cookie-Payload sah etwa so aus: user_id=123&expiry=1678886400. Der Server signierte genau diese Zeichenfolge mit einem geheimen Schlüssel, der auf dem Server gespeichert war. Das endgültige, an den Browser gesendete Cookie war data="user_id=123&expiry=1678886400"&signature="sha1=2a8b...".
Wenn der Benutzer eine nachfolgende Anfrage stellte, sendete sein Browser das Cookie zurück. Der Server nahm den data-Teil, berechnete die HMAC-Signatur mit seinem geheimen Schlüssel neu und verglich sie mit dem signature-Teil des Cookies. Wenn sie übereinstimmten, wusste der Server, dass die Benutzer-ID und das Ablaufdatum legitim waren und nicht manipuliert worden waren.
Lektion: HMAC ist eine fantastische Möglichkeit, manipulationssichere, „zustandslose“ Tokens oder Cookies zu erstellen, und bildet die Grundlage für viele Authentifizierungssysteme, einschließlich JSON Web Tokens (JWTs).
Die Banküberweisung, die nicht gekapert wurde
Eine E-Commerce-Plattform integrierte die API eines Zahlungsdienstleisters, um Auszahlungen an ihre Anbieter zu veranlassen. Der API-Aufruf war eine einfache JSON-Nachricht: {"vendor_id": "ven_abc", "amount": 500.00, "currency": "USD"}. Die Plattform machte sich Sorgen über einen Man-in-the-Middle-Angriff (MITM). Selbst über HTTPS, das den Verkehr verschlüsselt, könnte ein raffinierter Angreifer (in einigen theoretischen Szenarien, wie z. B. mit einer kompromittierten Zertifizierungsstelle) die Anfrage abfangen und ändern. Sie könnten den amount auf 50000.00 oder die vendor_id auf ihre eigene ändern.
Die API des Zahlungsdienstleisters erforderte, dass jede Anfrage mit HMAC-SHA512 signiert wurde. Die Plattform serialisierte den JSON-Payload in eine kanonische Zeichenfolge, berechnete die Signatur mit ihrem privaten API-Schlüssel und sendete sie in einem Authorization-Header. Die Server des Zahlungsdienstleisters führten genau die gleichen Schritte durch. Wenn ihre berechnete Signatur mit der im Header gesendeten übereinstimmte, wussten sie zwei Dinge mit Sicherheit: Die Anfrage kam von der legitimen Plattform (Authentizität) und vendor_id und amount waren während der Übertragung nicht geändert worden (Integrität).
Lektion: Bei Operationen mit hohem Einsatz bietet HMAC eine entscheidende Sicherheitsschicht, um zu garantieren, dass Absender und Inhalt einer Nachricht dem entsprechen, was du erwartest.
Häufige Fehler und Fallstricke
- Den geheimen Schlüssel leaken. Der Schlüssel ist alles. Wenn du ihn in clientseitigem JavaScript preisgibst, in ein öffentliches Git-Repository committest oder im Klartext protokollierst, ist deine Sicherheit dahin. Behandle ihn wie ein Passwort.
- Verwendung eines nicht-zeitkonstanten Vergleichs. Wenn du prüfst, ob die vom Benutzer bereitgestellte Signatur mit deiner berechneten übereinstimmt, kann ein Standard-String-Vergleich wie
if (a === b)eine Sicherheitslücke sein. Er gibt oftfalsezurück, sobald er ein nicht übereinstimmendes Zeichen findet. Dies erzeugt einen winzigen Zeitunterschied, den Angreifer messen können, um die Signatur Zeichen für Zeichen zu erraten. Das ist ein „Timing-Angriff“. Verwende immer eine dedizierte, „zeitkonstante“ Vergleichsfunktion aus einer Krypto-Bibliothek, die unabhängig davon, wo die Nichtübereinstimmung auftritt, die gleiche Zeit benötigt. - Die falschen Daten signieren. Sender und Empfänger müssen den HMAC über die exakt dieselbe Byte-Sequenz berechnen. Ein häufiger Fehler ist, dass eine Seite ein schön formatiertes JSON-Objekt signiert, während die andere die kompakte, einzeilige Version signiert. Oder eine Seite fügt einen nachgestellten Zeilenumbruch hinzu und die andere nicht. Ihr müsst euch auf ein kanonisches Nachrichtenformat einigen und euch daran halten.
- Replay-Angriffe vergessen. HMAC selbst hindert einen Angreifer nicht daran, eine gültige, signierte Nachricht abzufangen und sie einfach immer und immer wieder zu senden. Wenn diese Nachricht „zahle Bob 10 $“ lautet, willst du nicht, dass der Angreifer diese Zahlung 1000 Mal auslösen kann. Um dies zu verhindern, füge einen Wert hinzu, der sich mit jeder Anfrage ändert – wie einen Zeitstempel oder eine „Nonce“ (Nummer, die nur einmal verwendet wird) – innerhalb der zu signierenden Daten. Der Server kann dann den Zeitstempel überprüfen, um alte Anfragen abzulehnen, oder eine Liste der verwendeten Nonces führen, um Duplikate abzulehnen.
Warum du es auf dem Schirm haben solltest
Du solltest an HMAC denken, wann immer du es mit Kommunikation zu tun hast, der vertraut werden muss. Es geht nicht darum, die Daten geheim zu halten (das ist der Job von Verschlüsselung), sondern darum, sicherzustellen, dass die Daten legitim sind.
- Entwickelst oder nutzt du APIs? Besonders bei Webhooks (von Diensten wie Stripe, GitHub, Twilio) ist HMAC der Industriestandard zur Überprüfung der Authentizität einer Anfrage.
- Arbeitest du mit Authentifizierung? Viele Token-basierte Systeme, am bekanntesten JWTs, verwenden HMAC (z. B. den 'HS256'-Algorithmus), um den Payload des Tokens zu signieren und zu verhindern, dass Benutzer ihre eigenen Berechtigungen ändern.
- Musst du die Datenintegrität überprüfen? Wenn du Daten durch eine nicht vertrauenswürdige Umgebung (wie den Browser eines Benutzers in einem Cookie) leitest und sicherstellen musst, dass sie unverändert zurückkommen, ist HMAC dein Werkzeug.
Es ist ein grundlegender Baustein im Sicherheits-Werkzeugkasten eines Webentwicklers. Zu verstehen, wie es funktioniert, wird dich zu einem besseren, sicherheitsbewussteren Entwickler machen.
Tauche tiefer ein
- RFC 2104: HMAC: Keyed-Hashing for Message Authentication: Die ursprüngliche technische Spezifikation. Sie ist dicht, aber sie ist die ultimative Quelle der Wahrheit.
- Wikipedia: HMAC: Ein großartiger, übergeordneter Überblick über die Geschichte, die Designprinzipien und die Implementierungsdetails. (Englisch)
- OWASP: Replay Attack: Die Seite des Open Web Application Security Project zu Replay-Angriffen, einem entscheidenden Konzept, das man bei der Verwendung von HMAC verstehen muss.
- Stripe Docs: Checking webhook signatures: Ein praxisnaher Leitfaden von einem Unternehmen, das sich stark auf HMAC verlässt, um Transaktionen im Wert von Milliarden von Dollar abzusichern.
- Crypto 101: HMAC: Eine etwas zugänglichere Erklärung der kryptografischen Prinzipien hinter dem Design von HMAC.