// HEISE ONLINE — HARDWARE & GADGET
Passkeys in der Praxis – Teil 2: Registrierung und Authentifizierung
Die exakten Abläufe bei Registrierung und Anmeldung per Passkey sind komplex. Wer sie kennt, kann das Verfahren sicher und spezifikationsgemäß implementieren.
This article is also available in
English.
It was translated with technical assistance and editorially reviewed before publication.
Passkeys bestehen aus einem kryptografischen Schlüsselpaar, das bei der Registrierung erzeugt und bei jeder späteren Anmeldung wiederverwendet wird. Die genauen Abläufe – bei WebAuthn heißen sie Ceremonies – Registration und Authentication folgen dabei demselben Grundmuster: Der Server generiert eine Challenge, der Client ruft die WebAuthn API auf, der Authenticator signiert, und der Server validiert. Was sich unterscheidet, ist die Richtung: Registration (navigator.credentials.create()) erzeugt ein neues Credential und speichert den Public Key. Authentication (navigator.credentials.get()) beweist den Besitz des zugehörigen Private Key. Wer dieses Muster einmal wirklich versteht, kann alle Validierungsschritte direkt einordnen und erkennt sofort, wo eine Implementierung sicherheitskritische Abkürzungen nimmt.
Martina Kraus beschäftigt sich schon seit frühen Jahren mit der Webentwicklung. Das Umsetzen großer Softwarelösungen in Node.js und Angular hat sie schon immer begeistert. Als selbstständige Softwareentwicklerin arbeitet sie vornehmlich mit Angular mit Schwerpunkt auf Sicherheit in Webanwendungen.
Die Registration Ceremony nach §7.1 der W3C-WebAuthn-Level-2-Spezifikation beginnt auf dem Server. Bevor navigator.credentials.create() im Browser aufgerufen werden kann, muss der Server ein PublicKeyCredentialCreationOptions-Objekt zusammenstellen und an den Client senden. Dieses Objekt ist mehr als eine Konfigurationsdatei, denn jedes seiner Felder trifft eine Entscheidung, die sich auf Sicherheit und Nutzererfahrung auswirkt.
Bevor die einzelnen Felder im Detail besprochen werden, zeigt das folgende Listing, wie ein vollständiges PublicKeyCredentialCreationOptions-Objekt in der Praxis aussieht:
Das wichtigste Feld ist challenge. Diese ist ein Puffer aus kryptografisch zufälligen Bytes, den der Server generiert – mindestens 16 Bytes müssen es laut Spezifikation sein. In der Praxis haben sich allerdings 32 Bytes bewährt, weil sie einen deutlichen Sicherheitspuffer über dem Minimum bieten, ohne die Nutzlast spürbar zu vergrößern. Die Challenge wird serverseitig gespeichert, bis die Validierungsantwort eintrifft, und darf nur einmal verwendet werden. Ihr Zweck ist es, Replay-Angriffe zu verhindern: Ohne eine frische, serverseitig verifizierte Challenge könnte ein Angreifer eine abgefangene Authenticator-Antwort erneut einspielen.
Das rp-Objekt identifiziert die Relying Party (RP). Aus Teil 1 bekannt ist die Regel: Die id muss eine registrierbare Domain-Teilmenge der aktuellen Origin sein. In der Praxis bedeutet das, dass id entweder die exakte Domain oder eine übergeordnete Domain ist. shop.example.com kann demnach example.com als rpId nutzen, aber nicht umgekehrt. Der name ist ausschließlich für die Authenticator-UI relevant, nicht für die Sicherheit.
Das user-Objekt enthält Informationen über den registrierenden Nutzer. Hier lauert ein häufiger Fehler: Die user.id ist ein opaker – also für Client und Nutzer inhaltlich bedeutungsloser – Byte-Puffer, der keine personenbezogenen Daten enthalten soll, insbesondere keine E-Mail-Adresse und keinen Benutzernamen. Der Authenticator verknüpft das Credential intern mit dieser ID, und sie wird bei der Authentication im userHandle zurückgegeben. Eine zufällig generierte UUID ist die richtige Wahl. Die Spec schreibt außerdem vor, dass user.id zwischen 1 und 64 Bytes groß sein muss. Wer einen zu langen Puffer übergibt, bekommt einen TypeError. name und displayName werden nur in der Authenticator-UI angezeigt.
pubKeyCredParams ist ein Array, das angibt, welche Algorithmen der Server akzeptiert, in Prioritätsreihenfolge von oben nach unten. Die gängigen Einträge sind -7 für ES256 (ECD