Start · Sprachen · JavaScript · Referenz · AuthenticatorAttestationResponse.attestationObject

AuthenticatorAttestationResponse.attestationObject

Eigenschaft

Schreibgeschützter <code>ArrayBuffer</code> mit dem CBOR-kodierten Attestation-Objekt: Authenticator-Daten mit neuem öffentlichem Schlüssel plus optionaler Attestation-Signatur.

seit JavaScript Baseline: alle modernen Browser (seit 2019/2020) Kategorie: webapi

Signatur

AuthenticatorAttestationResponse.attestationObject

Beschreibung

Die schreibgeschützte Eigenschaft attestationObject des Interfaces AuthenticatorAttestationResponse enthält das Ergebnis einer WebAuthn-Registrierung, also eines Aufrufs von navigator.credentials.create({ publicKey }). Es handelt sich um einen ArrayBuffer mit CBOR-kodierten Daten, den der Client unverändert an den Server (die Relying Party) schickt.

Das dekodierte Objekt hat drei Felder:

  • fmt: das Attestation-Format, z. B. "none", "packed", "tpm", "android-key", "apple" oder "fido-u2f".
  • attStmt: die Attestation-Aussage (Signatur und ggf. Zertifikatskette). Ihr Aufbau hängt von fmt ab.
  • authData: die Authenticator-Daten. Sie enthalten den SHA-256-Hash der RP-ID, Flags, den Signaturzähler und die attestedCredentialData (AAGUID, Credential-ID und den öffentlichen Schlüssel im COSE-Format).

Die Prüfung der Attestation und das Auslesen des öffentlichen Schlüssels gehören auf den Server. Wer im Client nur den Schlüssel braucht, nimmt besser die Hilfsmethoden getPublicKey(), getPublicKeyAlgorithm() und getAuthenticatorData(), dann muss man CBOR nicht selbst parsen.

Rückgabewert

Typ
ArrayBuffer
Beschreibung
CBOR-kodiertes Attestation-Objekt mit den Feldern fmt, attStmt und authData.

Beispiele

Registrierung durchführen und attestationObject an den Server senden

// Hilfsfunktion: ArrayBuffer -> Base64URL
const toBase64Url = (buf) =>
  btoa(String.fromCharCode(...new Uint8Array(buf)))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

const register = async () => {
  const credential = await navigator.credentials.create({
    publicKey: {
      challenge: crypto.getRandomValues(new Uint8Array(32)), // in der Praxis vom Server
      rp: { name: 'Beispiel GmbH', id: location.hostname },
      user: {
        id: new TextEncoder().encode('user-42'),
        name: 'anna@example.com',
        displayName: 'Anna'
      },
      pubKeyCredParams: [
        { type: 'public-key', alg: -7 },   // ES256
        { type: 'public-key', alg: -257 }  // RS256
      ],
      attestation: 'direct'
    }
  });

  const { attestationObject, clientDataJSON } = credential.response;
  console.log(attestationObject instanceof ArrayBuffer, attestationObject.byteLength);

  await fetch('/webauthn/register', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      id: credential.id,
      attestationObject: toBase64Url(attestationObject),
      clientDataJSON: toBase64Url(clientDataJSON)
    })
  });
};

register().catch(console.error);
true 1024 // Länge variiert je nach Authenticator und Format

attestationObject mit einer CBOR-Bibliothek dekodieren

// z. B. mit dem npm-Paket "cbor-x"
import { decode } from 'cbor-x';

const inspectAttestation = (attestationObject) => {
  const { fmt, attStmt, authData } = decode(new Uint8Array(attestationObject));
  const flags = authData[32];
  console.log('Format:', fmt);
  console.log('User Present:', Boolean(flags & 0x01));
  console.log('User Verified:', Boolean(flags & 0x04));
  console.log('Attested Credential Data:', Boolean(flags & 0x40));
  console.log('attStmt-Felder:', Object.keys(attStmt));
};

// inspectAttestation(credential.response.attestationObject);
Format: packed User Present: true User Verified: true Attested Credential Data: true attStmt-Felder: [ 'alg', 'sig', 'x5c' ]

Ohne CBOR-Parser: Hilfsmethoden verwenden

const credential = await navigator.credentials.create({ publicKey: options });
const res = credential.response;

// Dieselben Informationen, die auch in attestationObject stecken
const authData = res.getAuthenticatorData();       // ArrayBuffer
const spki = res.getPublicKey();                    // ArrayBuffer (SPKI) oder null
const alg = res.getPublicKeyAlgorithm();            // COSE-Algorithmus, z. B. -7

console.log(authData.byteLength > 37, alg);

if (spki) {
  const key = await crypto.subtle.importKey(
    'spki', spki, { name: 'ECDSA', namedCurve: 'P-256' }, true, ['verify']
  );
  console.log(key.type);
}
true -7 public

// Wichtig · Fallstricke

  • Nur ein Container: Der Browser liefert die Bytes, prüft sie aber nicht. Challenge, Origin (aus clientDataJSON), RP-ID-Hash, Flags und Attestation-Signatur muss der Server prüfen, am besten mit einer etablierten Bibliothek wie SimpleWebAuthn, py_webauthn oder webauthn4j.
  • Attestation-Format hängt vom Wunsch ab: Ohne attestation: 'direct' oder 'enterprise' ist fmt meist "none" und attStmt leer. Viele Plattform-Authenticatoren und Passkey-Anbieter, die Schlüssel geräteübergreifend synchronisieren, liefern grundsätzlich keine Attestation. Die Registrierung ist dann trotzdem gültig.
  • Datenschutz: Die Attestation-Zertifikatskette kann das Gerätemodell verraten (AAGUID). Browser können deshalb nachfragen oder die Daten anonymisieren.
  • Serialisierung: ArrayBuffer lässt sich nicht direkt mit JSON.stringify umwandeln, das Ergebnis wäre {}. Vorher nach Base64URL kodieren oder credential.toJSON() nutzen, das es in neueren Browsern gibt.
  • Voraussetzungen: Funktioniert nur in einem sicheren Kontext (HTTPS oder localhost). Das Credential gibt es nur als Ergebnis von credentials.create(). Bei credentials.get() kommt eine AuthenticatorAssertionResponse ohne diese Eigenschaft zurück.
  • Kompatibilität: Die Eigenschaft funktioniert in allen aktuellen Browsern (Chrome, Edge, Firefox, Safari). Die Hilfsmethoden getPublicKey() und Co. gibt es erst in neueren Versionen.