Passkeys: why the password is finally being replaced
Passwords are a shared secret you keep retyping into whatever site asked. Passkeys replace it with a key pair whose private half never reaches the site at all.
On this page
The picture version
Five pictures for a reader who has only ever typed a password, following one bad afternoon: the fake bank page that caught your password and your six-digit code.
1 · The problem
The second factor got relayed too.
2 · Stop sharing the secret
The site keeps the half that’s useless to steal.
3 · The move that kills the relay
The attacker’s own domain gets signed into the answer.
4 · What it costs
The key not leaving the device is great until the device does.
5 · Keep this card
The whole thing on one index card.
Why it exists
A text says your bank has flagged a payment. The link looks right, the page looks right, so you type your password — and then the site asks for the 6-digit code from your authenticator app, which feels reassuring, so you type that too. Somewhere else, in real time, both were being retyped into the real bank’s login page. You did everything the security advice told you to do, including the second factor, and you were still robbed. That’s the scenario passkeys were built to make impossible, and it’s worth noticing why it worked: every single thing you were asked for was a thing you could type.
Every password-based login has the same shape: you type a secret, the site checks it against a stored hash, and if they match you’re in. That model has two failure modes that no amount of UX polish ever fixed.
The first is the breach. The site stores something derived from your password. If they hash it well, attackers crack a fraction. If they hash it badly — or in plaintext, which still happens — attackers crack all of it. Any password you reuse is now a key to other accounts.
The second, worse failure is phishing. You don’t even need a breach. You just need a fake login page that looks convincing. The user types the real password into the wrong site and hands it over. Two-factor codes via SMS or TOTP help, but they’re also typeable, so a phishing site that proxies through to the real site in real time can catch the code too. This is a well-worn path into modern accounts — not brute force, but social engineering plus a look-alike domain, exactly as in the bank text above.
Passkeys exist because the underlying problem is the shared secret itself. As long as authentication is “type a thing the server already knows,” the thing can be stolen, leaked, or fooled out of you. The fix is to stop sharing the secret at all.
Why it matters now
Apple, Google, and Microsoft shipped consumer passkey support across their platforms through 2022–2023, and large sites — GitHub and Amazon in 2023, Shopify since — added them. So this stops being theory the next time a site you use prompts you to “create a passkey,” and it stops being optional the first time you have to add WebAuthn to a product yourself.
The specific thing that changed is the price of a convincing fake. Building a look-alike login page, writing flawless copy in the target’s language, and running a real-time proxy that relays the victim’s password and one-time code to the genuine site used to take some skill; off-the-shelf phishing kits and generative tools have lowered the bar on each of those steps. Defenses that depend on the user noticing something is off degrade as the fakes get better. A credential that cannot be handed over — because the protocol never produces anything the user could type into the wrong page — doesn’t degrade at all. That’s the argument for a structural defense over a behavioural one.
The short answer
passkey = a key pair the site never gets the private half of + origin-bound challenge-response over WebAuthn
Picture to keep: instead of reciting a password, your phone signs a
one-off note saying “yes, this is me, and I am standing at
https://example.com” — and the signing pen never goes to the site, so
there is nothing to recite into the wrong page.
A passkey is a public/private keypair created for one site. The site stores only the public key. To log in, the site sends a random challenge; your device signs it with the private key; the site verifies with the public key. The private key is never sent to the site, and the browser ties the signature to the exact origin you’re on, so a fake site gets the wrong signature.
How it works
Follow the fake bank page from the hook, and fix it one break at a time.
Naive fix: stop sending a secret; send a signature. When you “create a
passkey” on a site, the browser generates a fresh keypair scoped to that
site’s origin (https://example.com). The private key is stored in the
platform’s secure store —
TPM
on Windows, Secure Enclave on Apple devices, equivalent on Android — and is
never handed to the site. The site keeps only the public key, plus a
credential ID it echoes back later to say “use this passkey.” Now a breach
of the site leaks public keys, which are worth nothing to an attacker.
But that alone doesn’t stop replay. If logging in meant sending a fixed signature, anyone who captured it once could resend it forever. Fix: sign a fresh challenge. The site sends a random value; the browser asks the platform to sign it, gated by a local user verification step (fingerprint, face, PIN). Each login produces a signature that is useless the next time.
But the phishing site can still relay. This is the break that kills TOTP:
the fake page simply forwards whatever the real site asks for, in real time,
and passes your answer along. If all the device signed were the challenge,
the fake page would hand that signature straight to the real bank and be
logged in as you. Fix: bind the signature to the origin. The authenticator signs the
challenge together with the browser’s own record of which site this is —
crucially, the origin the browser saw in the URL bar, not the one the page
claims — and the server checks both the signature and that the origin and site
identifier are the ones it expects. On
examp1e.com, the browser signs for examp1e.com, and the real site
verifies that signature against a key registered for example.com, and it
fails. The relay breaks because the attacker’s own domain is baked into the
thing being relayed, and no user could have typed their way past it.
This is the standard called WebAuthn (the browser API) running on top of CTAP2 (how the browser talks to the authenticator), both shepherded by the FIDO Alliance. “Passkey” is the consumer-friendly name for a passwordless credential of this kind. Some are synced across your devices via your platform account (iCloud Keychain, Google Password Manager) so losing your phone doesn’t lock you out; others are device-bound and stay on one authenticator.
That sync is the last break in the chain, and the seam worth showing. The key never leaving the device is great until the device goes in a river. Earlier FIDO2 deployments commonly used device-bound credentials — security through non-portability — and ordinary users hit a wall: lose the device, lose the account. Fix: end-to-end encrypted sync, so passkeys roam with your platform identity. And the cost of that fix: if your Apple ID or Google account is compromised, your passkeys travel with it. The honest read is that passkeys move the trust anchor from “every individual site’s password” to “your platform account,” which is a much smaller and much better-defended surface — but it is a surface, and “unphishable” describes the protocol, not your iCloud recovery flow.
There’s no published figure for what fraction of breaches passkeys would have prevented, and the claim doesn’t need one. The verifiable statement is the narrower one: the challenge-response is origin-bound, so credential phishing as a class doesn’t work against it.
You started with passkey = a key pair the site never gets the private half of + origin-bound challenge-response over WebAuthn. Which of those two halves did the fake
bank page in the hook actually run into? — the second. The site only ever holding the
public half is what survives a breach; binding the signature to the origin is
what survives you, on a bad day, on a convincing page. Passwords and
one-time codes have neither property, which is why no amount of user
training ever closed that particular hole.
Famous related terms
- WebAuthn —
WebAuthn = browser API + public-key credentials + origin binding— the W3C spec yournavigator.credentials.create()calls hit. - FIDO2 —
FIDO2 = WebAuthn + CTAP2— the umbrella name for the whole stack, browser plus authenticator. - TOTP —
TOTP = shared secret + current time → 6-digit code— better than nothing, still phishable because the code is typeable. - Hardware security key —
hardware key ≈ a device-bound passkey on a USB or NFC token— the original FIDO form factor; still useful where you don’t want cloud sync. - Password manager —
manager = encrypted vault + autofill— solves reuse and length, but the secret it autofills is still phishable if the user clicks through.
Going deeper
- The W3C WebAuthn spec — the primary source for the one question this post hand-waves: exactly what fields go into the signed blob, and what a verifier is required to check.
- The FIDO Alliance passkeys overview — the explainer for “what does a passkey mean as a product,” including where the synced-vs-device-bound distinction actually matters.
- Passkeys.dev — the rabbit hole for anyone who has to implement this, and the fastest way to see how much of the difficulty is account-recovery UX rather than cryptography.