Wie Hardware-Token-basierte Zwei-Faktor-Authentifizierung mit der WebAuthn-API funktioniert

Um eine höhere Sicherheit für die Anmeldung zu gewährleisten, setzen Webseiten eine Zwei-Faktor-Authentifizierung (2FA) ein, wobei derzeit häufig eine Smartphone-App oder Textnachrichten verwendet werden. Diese Mechanismen erschweren das Phishing, können es aber nicht vollständig verhindern – Benutzer können immer noch dazu verleitet werden, Codes weiterzugeben und auch SMS-Nachrichten sind nicht vor Abfangen geschützt.

Firefox 60 wird mit standardmäßig aktivierter WebAuthn-API ausgeliefert. Diese bietet eine Zwei-Faktor-Authentifizierung, die auf Public-Key-Kryptographie basiert und die gegen Phishing, wie wir es heute kennen, immun ist. In dieser Einführung erfahren Sie, wie Sie Millionen von Nutzern, die bereits im Besitz von FIDO U2F USB-Token sind, absichern können.

Registrierung von Zweit-Faktoren

Beginnen wir mit einem einfachen Beispiel: Eine Webseite fordert ein neues Credential an, das mit einem standardmäßigen, über USB angeschlossenen FIDO U2F-Gerät kompatibel ist. Es gibt viele dieser kompatiblen Token, die Yubikey, U2F Zero oder auch anders heißen:

const cose_alg_ECDSA_w_SHA256 = -7;

/* Die Challenge wird vom Server generiert. */
let challenge = new Uint8Array([21,31,105 /* 29 weitere, vom Server zufällig generierte Bytes */]);
let pubKeyCredParams = [{
  type: "public-key",
  alg: cose_alg_ECDSA_w_SHA256
}];
let rp = {
  name: "Test Website"
};
let user = {
  name: "Firefox User <firefox@example.com>",
  displayName: "Firefox User",
  id: new TextEncoder("utf-8").encode("firefox@example.com")
};

let publicKey = {challenge, pubKeyCredParams, rp, user};
navigator.credentials.create({publicKey})
  .then(decodeCredential);

Werden USB U2F-Token verwendet, warten alle kompatiblen Token, die mit dem System des Nutzers verbunden sind, auf eine Interaktion mit diesem. Sobald der Nutzer eines der Geräte berührt, wird ein neues Credential generiert und das Promise aufgelöst.

Die benutzerdefinierte Funktion decodeCredential()dekodiert die Antwort, um eine Schlüsselkennung zu erhalten. Diese ist entweder eine ID für das auf dem Gerät gespeicherte ECDSA-Schlüsselpaar oder das ECDSA-Schlüsselpaar selbst, verschlüsselt mit einem geheimen, gerätespezifischen Schlüssel. Der öffentliche Schlüssel, der zum ECDSA-Paar gehört, wird im Klartext gesendet.

Die Schlüsselkennung, der öffentliche Schlüssel und eine Signatur müssen vom Backend mittels der zufälligen Challenge verifiziert werden. Da ein Credential kryptographisch an die Webseite gebunden ist, die es angefordert hat, würde dieser Schritt fehlschlagen, sollten die ursprünglichen Angaben nicht übereinstimmen. Dies verhindert die Wiederverwendung von Credentials, die für andere Webseiten generiert wurden.

Sofern erfolgreich, werden Schlüsselkennung und öffentlicher Schlüssel fortan dem aktuellen Benutzer zugeordnet. Die WebAuthn-API setzt keine spezifische Browser-Benutzeroberfläche voraus, sodass es in der alleinigen Verantwortung der Webseite liegt, den Nutzern zu signalisieren, wann sie sich mit einem Token registrieren sollen.

Anmeldung mithilfe eines registrierten Zweit-Faktors

Wenn sich der Nutzer das nächste Mal auf der Webseite anmeldet, muss er den Besitz des zweiten Faktors nachweisen, der die Credentials im vorherigen Abschnitt erstellt hat. Das Backend sendet die gespeicherte Schlüsselkennung mit einer neuen Challenge an den Benutzer. Da allowCredentials ein Array ist, erlaubt es das Senden von mehr als einer Schlüsselkennung, falls mehrere mit einem Benutzerkonto registriert wurden.

/* Die Challenge wird vom Server generiert. */
let challenge = new Uint8Array([42,42,33 /* 29 weitere, vom Server zufällig generierte Bytes */]);
let key = new Uint8Array(/* … die Schlüsselkennung …  */);

let allowCredentials = [{
  type: "public-key",
  id: key,
  transports: ["usb"]
}];

let publicKey = {challenge, allowCredentials};

navigator.credentials.get({publicKey})
  .then(decodeAssertion);

Auch hierbei warten alle angeschlossenen USB U2F-Token auf die Interaktion des Nutzers. Wenn der Nutzer ein Token berührt, wird dieser versuchen, entweder die gespeicherte Schlüsselkennung mit der angegebenen ID zu finden oder diese mit dem internen, geheimen Schlüssel zu entschlüsseln. Ist dies erfolgreich, wird eine Signatur zurückgeschickt. Andernfalls wird der Authentifizierungsvorgang abgebrochen und muss von der Webseite wiederholt werden.

Nach der Dekodierung werden die Signatur und die Schlüsselkennung, mit der signiert wurde, an das Backend gesendet. Wenn der mit der Schlüsselkennung gespeicherte öffentliche Schlüssel in der Lage ist, die gegebene Signatur über die bereitgestellte Challenge zu verifizieren, wird der Nutzer erfolgreich angemeldet.

Ein-Faktor-Authentifizierung

Web Authentication definiert auch Mechanismen, mit denen man sich ohne Benutzername und Passwort nur mit einem einzelnen, sicheren Token einloggen kann – wie z.B. die vertrauenswürdige Ausführungsumgebung auf Ihrem Smartphone. In diesem Modus bestätigt Ihr Token, dass Sie diesen nicht nur besitzen, sondern auch, dass Sie als Person den Token entsperrt haben und zwar mit einem Passcode (etwas, das Sie kennen) und/oder mit biometrischen Daten (quasi etwas, das Sie sind).

Nach vorheriger Anmeldung auf einer Webseite könnte diese Ihnen dann erlauben, eine nahtlose Authentifizierung bei einer Webanwendung auf Ihrem Desktop durchzuführen – und zwar ganz einfach, indem Sie auf eine Eingabeaufforderung antworten, die auf Ihrem Smartphone angezeigt wird.

Die momentan eingesetzten FIDO U2F-Token sind noch nicht ausgereift genug, um dies zu ermöglichen; die nächste Generation von Token allerdings schon, sodass Webentwickler mit diesen FIDO 2.0-Token über die Web Authentifizierung interagieren können werden.

WebAuthn – bald in Ihrem Firefox

Dies war eine sehr kurze Einführung in die Welt der Web-Authentifizierung und sie verzichtet bewusst auf spezifische Details wie CBOR-Encoding und COSE-Key-Formate sowie weitere Parameter, die an die .create()und .get()Funktionen übergeben werden können.

Wir möchten Entwickler dazu ermutigen, mit WebAuthn zu experimentieren und es Nutzern zu ermöglichen, ihre Anmeldungen mit zwei Faktoren abzusichern, sobald die API verfügbar ist. Uns sind zum Zeitpunkt der Erstellung noch keine WebAuthn-U2F Polyfill-Bibliotheken bekannt, aber wir hoffen, dass sie bald verfügbar sein werden. Sollte Ihnen bereits etwas Vielversprechendes über den Weg gelaufen sein, schreiben Sie uns gerne einen Kommentar.

Standardisierte Zwei-Faktor-Authentifizierung ins Web zu bringen, stellt für uns einen bedeutenden Meilenstein dar. Public-Key-Kryptographie schützt unsere Daten, wenn sie über das TLS-Protokoll im Internet unterwegs sind – und jetzt können wir damit auch noch das Phishing viel, viel schwerer machen. Probieren Sie WebAuthn doch am besten gleich in Firefox Nightly aus!

Ein letzter Hinweis zum Testen

Die WebAuthn API ist ein leistungsstarkes Feature. Daher kann es nur in einem Secure Context verwendet werden und wenn es in einem Frame aufgerufen wird, nur dann, wenn alle Frames vom gleichen Ursprung (Origin) wie das übergeordnete Dokument stammen. Das bedeutet, dass Sie wahrscheinlich auf Sicherheitsfehler stoßen werden, wenn Sie auf einigen populären Test-Websites (wie z.B. jsfiddle.net) damit experimentieren.