πŸ›‘οΈ CSP is active β€” the hijack script never loads, your registrations go through unmodified
← All demos Β· Passkeys Demo 2 of 4 Β· CSP

Gesture-Preserving Forgery

A malicious script hooks navigator.credentials.create. You see your real biometric prompt and complete a real ceremony β€” and the script silently substitutes its own keypair before the credential reaches the server.

Protected β€” the hook is never installed; biometric ceremonies bind credentials to your authenticator as expected

Set up a passkey

Logged in as …. Click Register passkey and complete the biometric / security key prompt your browser shows you. Watch the activity log and credentials list as it happens.

βœ… Hook never installed. CSP script-src 'self' rejected the cross-origin script that contained the monkey-patch. navigator.credentials.create is unmodified, so when you click Register you get the real attestation from your authenticator and the real credential is what reaches the server. The violation is reported to Report URI.

If you tested in unprotected mode first, any forged credentials from that run still live in your demo session β€” switching modes does not wipe server state. Click Reset demo to start clean.
CSP header sent with this page
default-src 'self'; script-src 'self'; connect-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; report-uri https://helios.report-uri.com/r/t/csp/enforce; report-to default
What's happening

CSP script-src 'self' blocks the cross-origin script that wanted to install the monkey-patch. With the hook never in place, navigator.credentials.create is the browser's own implementation, which is bound to the authenticator securely. The credential you register is the credential the server stores β€” origin binding, challenge binding, and authenticator binding all hold.

Permissions Policy doesn't help here either: publickey-credentials-create governs whether the API may be called at all, but in 4b the call is initiated by the page itself β€” exactly where you'd want it allowed. The defence is keeping untrusted code out of the page in the first place.

Credentials registered to your account

Live β€” polls /passkeys/api/credentials.php every second. Forgeries from this attack are submitted with the page's normal label (legitimate), since the server can't tell them apart from real ones β€” the page detects the forgery via the in-memory attacker stash and flags it for you.

  • Loading…

Activity log

Real-time trace of legitimate ceremony steps, hook installation and substitution events, and CSP violations.

    ← Demo 1 Demo 3 β†’
    πŸ›‘οΈ CSP blocked 0 attempt(s)