惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
Hacker News: Ask HN
Hacker News: Ask HN
T
Threatpost
WordPress大学
WordPress大学
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 【当耐特】
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
S
SegmentFault 最新的问题
月光博客
月光博客
Latest news
Latest news
博客园 - Franky
T
Threat Research - Cisco Blogs
有赞技术团队
有赞技术团队
博客园_首页
T
The Exploit Database - CXSecurity.com
The Cloudflare Blog
人人都是产品经理
人人都是产品经理
C
Cybersecurity and Infrastructure Security Agency CISA
S
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
爱范儿
爱范儿
Security Latest
Security Latest
Scott Helme
Scott Helme
博客园 - 聂微东
T
Tor Project blog
美团技术团队
IT之家
IT之家
Stack Overflow Blog
Stack Overflow Blog
B
Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
G
Google Developers Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
AWS News Blog
AWS News Blog
Jina AI
Jina AI
Vercel News
Vercel News
小众软件
小众软件
T
Tenable Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Forbes - Security
Forbes - Security
aimingoo的专栏
aimingoo的专栏
O
OpenAI News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Last Watchdog
The Last Watchdog
Cloudbric
Cloudbric
AI
AI

Filippo Valsorda

Vulnerability Reports Are Not Special Anymore Quantum Computers Are Not a Threat to 128-bit Symmetric Keys A Cryptography Engineer’s Perspective on Quantum Computing Timelines Turn Dependabot Off Inspecting the Source of Go Modules go.sum Is Not a Lockfile Building a Transparent Keyserver The 2025 Go Cryptography State of the Union Claude Code Can Debug Low-level Cryptography The Geomys Standard of Care A Retrospective Survey of 2024/2025 Open Source Supply Chain Compromises
Opaque, Interoperable Passkey Records (and a Go API)
2026-07-21 · via Filippo Valsorda

Passkeys are the most important thing happening in information security right now because they are the only principled solution to the overwhelming effectiveness of phishing attacks. Just like memory safety is the only principled solution to memory corruption attacks.

Unfortunately, implementing them on the server side can appear more complex than using password hashes. Part of this is unavoidable because passkeys require interaction with the browser to get their phishing resistance properties. Part of it, however, could be abstracted away a little more effectively by defining interoperable passkey record encodings.

The WebAuthn specification defines a credential record as an abstract concept with a number of components such as type, id, publicKey, backupState, transports, and other flags. Google recommends a database table with a Credential ID primary key, and public_key, backed_up, and transports columns. Adam Langley’s stellar Tour of WebAuthn similarly recommends a cred_id primary key, and separate public_key_spki and backed_up columns.

All guidance recommends using libraries to handle WebAuthn authentication, but that still leaves applications with a potentially non-interoperable database schema. It might also be impractical to defer the whole authentication flow and database interaction to a library or framework.

Interoperable, well-specified passkey records that the application can handle as opaque strings, like password hashes, can be a middle-ground abstraction layer.

c2sp.org/passkey-record is a specification proposal which borrows the syntax of Password Hashing Competition (PHC) Strings and reuses the existing authenticator data encoding for the bulk of the work. A record looks like this:

$webauthn$v=1$transports=hybrid+internal$<base64 authenticator data>

The payload is the authenticator data, a CTAP2 CBOR encoding of most of the credential record fields that is already specified by WebAuthn and included in the JSON encoding of an AuthenticatorAttestationResponse (which is the return type of navigator.credentials.create() even if attestation is not in use). Transports are the only missing field, and they are stored as PHC parameters.

The application is then only in charge of keeping track of what passkey records are associated with a user account, which is a task that is familiar to web developers because it is not dissimilar to implementing password authentication (except there are multiple passkeys per account). These opaque strings can be passed to a library to verify the login assertion (or to generate a registration request with the appropriate excludedCredentials).

With a well-specified interoperable storage format, it will hopefully be possible to switch passkey library (or even backend language) while preserving a credentials database.

Other fields you might want to store

Along with the passkey record, applications might still wish to store metadata fields like a user-selected nickname and creation and last use timestamps, to provide pretty passkey management UIs. None of these require any special treatment by the WebAuthn library.

The one exception is the backed up state flag. Passkeys report to the server whether they are backed up (e.g. to iCloud Keychain or a Google account), and servers can use that signal to suggest removing a password from an account. This flag can change across logins, while the passkey record is immutable, so it would have to be stored separately and updated on every login. I think this logic is overrated for the average website, which will keep supporting email password resets anyway.

A potential crypto/passkey API

Building on top of these passkey records, I drafted a potential crypto/passkey stateless Go package API.

The registration flow is

  1. call RelyingParty.NewRegistration with the logged-in (or otherwise identified) user details and any existing passkey records
  2. pass the returned JSON to parseCreationOptionsFromJSON() and then to navigator.credentials.create()
  3. pass the returned JSON-encoded PublicKeyCredential to RelyingParty.Register
  4. store the returned passkey record in the database

The login flow is

  1. call RelyingParty.NewLogin while generating the log-in page
  2. store the returned request in a key-value cache with a short TTL, under RequestID(request), and pass the returned JSON to parseRequestOptionsFromJSON() and then to navigator.credentials.get()
  3. pass the returned JSON-encoded PublicKeyCredential to Inspect, and use the returned requestID to retrieve the request from the key-value cache and use the returned userID to retrieve the passkey records from the database
  4. pass the JSON PublicKeyCredential, the request, and the passkey records to RelyingParty.Login

The application is in charge of

  • associating an opaque, permanent, privacy-preserving user ID with each user;
  • storing passkey records associated with a user; and
  • caching request challenges produced by RelyingParty.NewLogin.

The library provides JSON values that can be passed straight to parseCreationOptionsFromJSON() and parseRequestOptionsFromJSON(), and accepts JSON values returned by calling JSON.stringify() on a PublicKeyCredential.

This is optimized for the discoverable credential flow (a.k.a. passkeys, where the authenticator stores and provides to the server the user ID) but the RelyingParty.NewLoginForUser method can also be used for second-factor flows or re-authentication prompts. The API works for both modal and conditional UI (autofill) flows.

There are a few helpers to extract information from the passkey record (AAGUID, BackedUp) and from the JSON-encoded PublicKeyCredential (ResponseBackedUp).

Currently, there is no implementation; I would like to get feedback on the passkey record format and on the Go API, before potentially making a proposal for Go 1.28.

On duplicate Credential IDs

One thing you can’t do with this storage model is ensure that different accounts don’t share passkeys with the same Credential ID, which the spec says you SHOULD do.

The reason for that check is avoiding attacks where you look up a credential by its ID and land at the wrong public key or user ID because the attacker intentionally injected a colliding Credential ID through their own account.

This attack simply can’t happen if you don’t have a Credential ID index in the first place! The index is only needed to mitigate an attack introduced by the existence of the index.

Login attempts carry the user ID, and if you use that to look up the user’s passkey records to verify the login against, it doesn’t matter if some other user has a passkey with the same Credential ID, just like it doesn’t matter if two users share a password.

Don’t let the attacker dictate your PRIMARY KEY and you won’t have PRIMARY KEY collision attacks.

For more Go API previews, follow me on Bluesky at @filippo.abyssdomain.expert or on Mastodon at @filippo@abyssdomain.expert.

The picture

More from this year’s CENTOPASSI (a GPS-tracked motorcycle competition involving careful planning, 100 coordinates, and 1700 km of secondary roads over three and a half days). Here’s a glimpse of Castel del Monte (AQ), after climbing down from a deserted and still snowy Campo Imperatore.

A medieval town is seen from above, framed by trees. In the distance, woods, hills, and then a snow-streaked peak. The sky is grey and overcast.

My work is made possible by Geomys, an organization of professional Go maintainers, which is funded by Ava Labs, Teleport, Datadog, Tailscale, and Sentry. Through our retainer contracts they ensure the sustainability and reliability of our open source maintenance work and get a direct line to my expertise and that of the other Geomys maintainers. (Learn more in the Geomys announcement.) Here are a few words from some of them!

Teleport — For the past five years, attacks and compromises have been shifting from traditional malware and security breaches to identifying and compromising valid user accounts and credentials with social engineering, credential theft, or phishing. Teleport Identity is designed to eliminate weak access patterns through access monitoring, minimize attack surface with access requests, and purge unused permissions via mandatory access reviews.

Ava Labs — We at Ava Labs, maintainer of AvalancheGo (the most widely used client for interacting with the Avalanche Network), believe the sustainable maintenance and development of open source cryptographic protocols is critical to the broad adoption of blockchain technology. We are proud to support this necessary and impactful work through our ongoing sponsorship of Filippo and his team.