Zum Inhalt springen
ecdsa.com
Node.js · WebCrypto

SimpleWebAuthn

SimpleWebAuthn von Matthew Miller, die meistgenutzte Passkey-Bibliothek für TypeScript: Generatoren für Optionen und Verifizierer für Antworten, die standardmäßig Benutzerverifizierung verlangen, Assertion-Signaturen nur als DER akzeptieren und Rückgänge des Zählers ablehnen.
Lizenz
MIT
Geprüfte Version
14.0.3 ·
Quellcode
github.com/MasterKale/SimpleWebAuthn
Dokumentation
simplewebauthn.dev/docs/

Kompatibilität auf einen Blick

WebAuthn

SimpleWebAuthn teilt WebAuthn auf zwei Pakete auf: @simplewebauthn/browser kapselt navigator.credentials, und @simplewebauthn/server erzeugt die Optionen, die eine Relying Party sendet, und prüft, was zurückkommt – Attestierungsformate, COSE-Schlüssel, Zähler und Signaturen. Für die Prüfung wurde das Server-Paket mit einem Software-Authenticator angesteuert, der Standard-JSON für Registrierung und Assertion erzeugte – dieselben Bytes wie für die Seiten zu Python, Go und Java.

Der häufigste Integrationsfehler ist eine Abweichung zwischen dem, was die Optionen anfordern, und dem, was die Prüfung verlangt. generateRegistrationOptions() setzt standardmäßig userVerification: "preferred", beide Verifizierer dagegen standardmäßig requireUserVerification: true: Eine Assertion von einem Authenticator, der nur die Benutzeranwesenheit nachwies – ein Sicherheitsschlüssel ohne PIN –, wurde mit User verification required, but user could not be verified abgelehnt. Entscheide dich einmal und sorge dafür, dass beide Seiten dasselbe verlangen, wie es das Beispiel tut.

Assertion-Signaturen für ES256 müssen ASN.1-DER sein, wie es die WebAuthn-Spezifikation verlangt. Eine rohe r‖s-Signatur mit 64 Byte landet trotzdem im ASN.1-Parser und scheitert mit einer Meldung, die von den Signaturbytes abhängt – Too big integer, End of input reached before message was fully decoded oder Data does not match to ECDSASigValue ASN1 schema. waren bei 60 frischen Credentials die häufigsten –, und keine davon erwähnt die Kodierung. Authenticators und Passkey-Anbieter erzeugen DER; rohe Signaturen deuten daher meist auf einen selbst geschriebenen Client oder eine Testumgebung hin, die sign() von WebCrypto direkt aufgerufen hat. Konvertiere sie mit dem Konverter DER ⇄ Raw. Eine DER-Signatur mit High-S bestand die Prüfung.

Ein Signaturzähler unter dem gespeicherten Wert ist ein Fehler, keine Warnung: Die Prüfung warf Response counter value 3 was lower than expected 5, und ein Zählerstand von 0 nach einer gespeicherten 5 scheiterte genauso. Nur ein Credential, das immer 0 gemeldet hat – wie es synchronisierte Passkeys tun –, besteht mit 0. Speichere den empfangenen Zählerstand und entscheide selbst, ob ein Rückgang das Credential sperrt oder nur eine Benachrichtigung auslöst. Version 14 führt in der Standardliste pubKeyCredParams ML-DSA-44 (COSE -48) an erster Stelle, vor Ed25519 (-8), ES256 (-7) und RS256 (-257), und das Ed25519-Credential funktionierte durchgängig, von der Registrierung bis zur Prüfung.

Dekodiere mit dem WebAuthn-Inspektor, was ein Browser tatsächlich gesendet hat, probiere Abläufe ohne echten Authenticator in der Passkey-Sandbox aus und bestimme Authenticator-Modelle anhand der AAGUID mit dem AAGUID-Lookup.

Fakten und die Prüfungen dahinter

Nichts im Folgenden ist aus einer Dokumentation abgeschrieben. Das Prüfskript content/libraries/verify/simplewebauthn/run.sh hat SimpleWebAuthn 14.0.3 unter Node.js 24.22.0 am 20. Oktober 2026 im Container-Image node:24.22.0-bookworm-slim ausgeführt; die Seite muss mit seiner Ausgabe übereinstimmen, und jeder Fakt führt die zugehörigen Zeilen auf.

  • Standard-COSE-Algorithmen

    -48-8-7-257

    generateRegistrationOptions() listet die COSE-Algorithmen -48 (ML-DSA-44), -8 (Ed25519), -7 (ES256) und -257 (RS256) in dieser Reihenfolge auf.

    Von der Prüfung aufgezeichnet
    fact webauthn-default-algorithms = -48, -8, -7, -257
  • Standard-Attestierung

    none

    Die Übermittlung der Attestierung steht standardmäßig auf none.

    Von der Prüfung aufgezeichnet
    fact webauthn-attestation = none
  • Standard-Benutzerverifizierung

    preferred

    authenticatorSelection.userVerification steht standardmäßig auf preferred.

    Von der Prüfung aufgezeichnet
    fact webauthn-user-verification = preferred
  • Standardwert für Resident Key

    preferred

    authenticatorSelection.residentKey steht standardmäßig auf preferred.

    Von der Prüfung aufgezeichnet
    fact webauthn-resident-key = preferred
  • Benutzerverifizierung standardmäßig erzwungen

    yes

    Beide Verifizierer verlangen das UV-Flag, sofern nicht requireUserVerification: false übergeben wird: Eine Registrierung und eine Assertion, bei denen nur UP gesetzt war, wurden abgelehnt – die Registrierung mit User verification was required, but user could not be verified, die Assertion mit User verification required, but user could not be verified.

    Von der Prüfung aufgezeichnet
    fact webauthn-uv-enforced-by-default = yes
    evidence webauthn-uv-enforced-by-default: registration with the UV flag clear: throws User verification was required, but user could not be verified
  • Kodierung der Assertion-Signatur

    der

    Eine korrekte ES256-Assertion mit ASN.1-DER-Signatur bestand die Prüfung.

    Von der Prüfung aufgezeichnet
    fact webauthn-assertion-encoding = der
  • Assertion-Signatur als rohes r‖s

    rejected

    Dieselbe Assertion mit roher r‖s-Signatur (64 Byte) warf einen ASN.1-Parsing-Fehler, dessen Wortlaut von den Signaturbytes abhängt.

    Von der Prüfung aufgezeichnet
    fact webauthn-raw-signature = rejected
  • Assertion-Signatur mit High-S

    accepted

    Eine DER-Signatur, in der s durch n − s ersetzt war, bestand die Prüfung.

    Von der Prüfung aufgezeichnet
    fact webauthn-high-s = accepted
  • Rückläufiger Signaturzähler

    rejected

    Ein Zählerstand unter dem gespeicherten Wert warf Response counter value 3 was lower than expected 5; ein Zählerstand von 0 nach einer gespeicherten 5 wurde ebenfalls abgelehnt, während 0 nach einer gespeicherten 0 die Prüfung bestand.

    Von der Prüfung aufgezeichnet
    fact webauthn-counter-regression = rejected
    evidence webauthn-counter-regression: counter 0 after a stored 5: throws Response counter value 0 was lower than expected 5; counter 0 after a stored 0: verified
  • Ed25519-Credentials

    supported

    Ein Ed25519-Credential (COSE -8) ließ sich erfolgreich registrieren und zur Authentifizierung verwenden.

    Von der Prüfung aufgezeichnet
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Verwendung

Erzeuge Optionen für Registrierung und Authentifizierung, die dieselbe Richtlinie zur Benutzerverifizierung anfordern, die die Verifizierer standardmäßig durchsetzen.
import { generateAuthenticationOptions, generateRegistrationOptions } from "@simplewebauthn/server";

const rpID = "example.com";

// Registration: the options ask for UV only as "preferred"...
const registration = await generateRegistrationOptions({
  rpName: "Example",
  rpID,
  userName: "user@example.com",
  authenticatorSelection: { residentKey: "required", userVerification: "required" },
});

// ...while verifyRegistrationResponse() and verifyAuthenticationResponse()
// require the UV flag by default. Ask for "required" up front (as here), or pass
// requireUserVerification: false when a security key without a PIN is acceptable.
const authentication = await generateAuthenticationOptions({ rpID, userVerification: "required" });

console.log("algorithms:", registration.pubKeyCredParams.map((param) => param.alg).join(", "));
console.log("uv:", registration.authenticatorSelection?.userVerification, authentication.userVerification);
console.log("challenge bytes:", Buffer.from(authentication.challenge, "base64url").length);

Die Snippet-Testumgebung führt jedes Beispiel unverändert im Container-Image node:24.22.0-bookworm-slim aus, und der Lauf schlägt fehl, wenn die Ausgabe nicht übereinstimmt.

Einrichtung
npm init -y >/dev/null && npm install --no-audit --no-fund @simplewebauthn/server@14.0.3
Befehl
node usage.mts
Ausgabe muss passen (regulärer Ausdruck)
algorithms: -48, -8, -7, -257\nuv: required required\nchallenge bytes: 32

Bibliotheken