Muckturnier

News

Ordentliche Sicherheit für Anmeldungscodes

Anmeldungscodes werden ab dem nächsten Release (4.0.0, t. b. a.) vernünftig abgesichert sein. Ja, kein Mensch will einen Anmeldungscode fälschen. Nein, das ist auch noch nie vorgekommen. Aber es geht hier ja nicht um „für ein Hobbyprojekt reicht’s schon“ oder „funktioniert doch“ – es geht darum, vernünftige Code-Qualität und eine vernünftige, professionelle Implementierung vorzulegen. Das gebietet die Programmierer-Ehre. Soll ja keiner sagen können, ich würde schlechten Code schreiben :-P

Was wir hatten, und warum das schlecht war

Ich dachte mir damals folgendes: Es braucht, zusätzlich zur eigentlichen Datenübertragung, einen Sicherheitsmechanismus. Weil sonst ist ein Muckturnier ausgebucht, und einer kommt auf die Idee, sich die Spezifikation anzuschauen (die ist einfach), und sich seinen eigenen Anmeldungscode zu bauen (auch das ist einfach). Und dann steht jemand da, und behauptet, er wäre ja vorangemeldet gewesen – obwohl das nicht stimmt. Oder – und sowas würde ich persönlich machen: Bauen wir einfach mal so einen Code, nur, um den Entwickler zu ärgen, bzw. ihm vor Augen zu führen, dass sein Kram nichts taugt.

Also: Wie machen wir das, einfach, und mit wenig Zeichen (damit die QR-Codes kompakt bleiben)? Augenscheinlich braucht es einen kryptographischen Hash, eine Prüfsumme der Nachricht. Für die Netzwerkfunktionalität nutze ich intern MD5. Da geht es zwar nicht um Sicherheit, sondern einfach nur darum, dass alle Clients auf demselben Stand sind – aber dann nehmen wir halt das. MD5 ist schnell und leicht zu berechnen, und der Hash ist vor allem kurz. Wichtig, weil die QR-Codes sollen ja kompakt bleiben. Also nehmen wir einfach einen geheimen Schlüssel und berechnen den MD5-Hash aus dem Schlüssel und dem Nachrichtentext, und hängen den Hash an die Nachricht an.
Ja, ich wusste auch zu dem Zeitpunkt bereits, das MD5 in einem kryptographischen Kontext schon lang gebrochen wurde, aber für so kurze Nachrichten und für den Zweck wird’s schon reichen. Oder?!

Warum war das ein Trugschluss bzw. eine schlechte Idee?

Wie man es richtig macht

Wenig überraschend: Für exakt diesen Einsatzzweck haben schlaue Leute ein Verfahren definiert, das sicher ist und funktioniert: Hash-based Message Authentication Codes (HMAC), standardisiert in RFC 2104.

HMAC basieren ebenfalls auf einer Hash-Funktion, das Verfahren behebt aber das Problem der potenziellen Anfälligkeit für einen Length-Extension-Angriff. Der geheime Schlüssel wird bei dem Verfahren zweistufig mit dem Nachrichtentext verwoben: Zunächst wird der Schlüssel mit einem „inneren Pad“ vermischt, dann wird das Ergebnis zusammen mit der Nachricht gehasht. Dann wird der Schlüssel mit einem „äußeren Pad“ vermischt, und das Ergbnis wird dann mit dem Ergbnis des ersten Hash-Vorgangs erneut gehasht. Das mutet erwas umständlich an, sorgt aber dafür, dass mit diesem Verfahren selbst eine an sich unsichere Hash-Funktion wie MD5 auch Stand jetzt noch sicher angewendet werden könnte.

Aber wenn wir es schon anders machen, dann machen wir es gleich „richtig“ richtig. Die jetzt benutzte HMAC-Hash-Funktion ist SHA-256. Der resultierende Hash von SHA-256 ist allerdings erheblich länger, als der von MD5. Beispiel: Die hexadezimale Repräsentation des MD5-Hashs von „Muckturnier.org“ ist 91765900c6885d64ef95987f05123140. Berechnet man den Hash mit SHA-256, ist das Resultat b4d08106b5b663dc4b4d18275fe8fcdb26e46abd3ab0ca27621ddcc7f9875579. Jetzt sollen ja aber die QR-Codes kompakt bleiben.
Netterweise kann man den Hash einfach abschneiden, indem man nur die ersten paar Bytes benutzt („Truncation“) – und das Ergebnis ist, vorausgesetzt, man benutzt genügend Bytes, immer noch kryptographisch sicher. Zum Einsatz kommen die ersten 16 Bytes – das ist dieselbe Länge, die der MD5-Hash vorher auch hatte. Das sind 128 Bits eines SHA-256-Hashs – und damit sprechen wir hier nicht von einem Kompromiss, sondern von Enterprise-Level Security.

Weiterhin wird per Standard jetzt kein zufälliger Sicherheitsschlüssel aus einer Untergruppe von ASCII-Zeichen mehr generiert, sondern 32 kryptographisch sicher zufällig generierte Bytes (irgendwann erkennt man mal den Unterschied zwischen Strings und Byte Arrays, was das eine und was das andere ist, und wann man was davon benutzt ;-) Das führt dazu, dass der Sicherheitsschlüssel (der jetzt auch nur noch auf explizite Anfrage nach einer Warnung geändert werden kann) 256 Bits an kryptograpsch sicherer Entropie darstellt – und das ist ein astronomisch großer Wert, der noch nicht einmal rein theoretisch gebrochen werden kann. Auch nicht mit Quantencomputern.

Wie Anmeldungscodes jetzt funktionieren

Die Spezifikation des Protokolls für Anmeldungscodes war von vornherein gut designt. Die Struktur ist exakt gleich geblieben. Nur kommen jetzt statt eines „nackten“ MD5-Hashs der Eingabedaten zur Absicherung die ersten 16 Bytes eines HMAC-SHA-256-Hashs des gesamten kodierten Datagrams zum Einsatz, unter Verwendung eines extrem sicheren Schlüssels. Besagter Schlüssel wird nach wie vor für jedes Turnier neu generiert und kommt nur ein Mal zum Einsatz.

Damit ist das Sicherheitskonzept der Anmeldungscodes jetzt keine undurchdachte „Wird schon passen dafür“-Lösung, sondern – auf absehbare Zeit – wirklich kryptographisch sicher. Das würde in dieser Form jetzt auch einem kryptographischen Audit standhalten. Und das, obwohl die Codes von der Struktur her genauso aussehen, wie bisher.

Das neue Protokoll ist nicht abwärtskompatibel

Es gibt zwei substanzielle Änderungen:

Bedingt dadurch ist das neue Anmeldungs-Code-Protokoll nicht abwärtskompatibel. Selbst, wenn man einen bereits gespeicherten Sicherheitsschlüssel in seine Base64-Darstellung konvertieren würde, könnten vorher erstellte Anmeldungscodes nicht verarbeitet werden, da sich ja die Hash-Funktion geändert hat. Das Einführen einer Kompatibilitätsschicht (also das weitere Unterstützen von Anmeldungscodes, die mit Protokollversion 2 erzeugt wurden) erscheint mir nicht sinnvoll, da die Codes ja nur für dieses eine Turnier genutzt werden. Später tauchen ja keine alten Codes mehr auf.

Also bitte beachten: Wenn bereits Anmeldungscodes generiert wurden, dann das Turnier auch mit der bisherigen Version des Programms auswerten, und erst hinterher updaten. Darauf wird aber im Release Announcement nochmal hingewiesen.

Da haben wir ja gerade nochmal die Kurve gekriegt ;-)