-
Notifications
You must be signed in to change notification settings - Fork 47
Description
Proposal: WebAuthn-agnostic device browser binding for Secure Payment Confirmation
This is a high-level proposal for adding device-binding-like capabilities, in the form of browser bound keys (BBKs), to Secure Payment Confirmation without relying on WebAuthn (at either the client or authenticator level). At time of writing it is still exploratory and should not be taken as a concrete plan until the WG has discussed and decides it is the appropriate direction to move in. The proposal is also missing detail that will need to be filled in before it could be spec'd / implemented.
Background / Motivation
Secure Payment Confirmation builds on top of WebAuthn to make it more useful for the payments industry. Today, that comes via (at least) three added features on-top of WebAuthn:
- Providing a consistent and clear browser-hosted transaction dialog for the user to review their transaction details and agree to the amount, payee, etc.
- Including transaction details in the WebAuthn cryptogram (as part of ClientDataJSON), with a goal of helping with payment requirements such as Dynamic Linking.
- Supporting cross-origin authentication flows, such that a merchant or PSP can initiate an SPC authentication in their domain with a relying party of (for example) the issuing bank, without having to iframe or redirect to the issuing bank.
As of late 2023/early 2024 however, we are hearing strongly from our payments partners that a significant required feature is missing - device binding. As WebAuthn passkeys can now be synced, it can be argued that they no longer meet strict 2FA requirements (being no longer a signal of device possession), and so SPC (like WebAuthn) is reduced to a single factor (biometric or possession, depending on the authentication method used).
WebAuthn/FIDO have been exploring device binding for years, and have a proposed solution in the form of DPK/SPK. However, progress in making DPK/SPK widely available/deployed appears slow. As of May 2024 no platform authenticator ships DPK/SPK, and support is not (as far as the author knows) expected across the major platform authenticators this year - not is there any commitment or expectation from any platform authenticator that they will ship DPK/SPK.
Proposal
Given the timescales and uncertainty involved with WebAuthn's SPK, develop an SPC-specific device browser binding solution that can be experimented with and potentially launched during 2024.
As a rough outline, during SPC authentication in the case where the passed-in WebAuthn credential is available and the user has accepted the SPC transaction dialog, the user agent would:
- Retrieve or create a device-specific identifier (a public-private keypair) for the unique tuple of (Relying Party ID, credential ID). In this proposal, we refer to these as browser-bound keys (BBKs).
- Include the BBK in the ClientDataJSON. (This step proves that the browser created the BBK, defeating person-in-the-middle attacks).
- Upon successful completion of the WebAuthn authentication by the user, also sign over the ClientDataJSON with the BBK private key, and include both signatures in the output cryptogram.
The expectation is that on receiving a never-before seen BBK, a relying party might do an additional step-up (e.g., SMS OTP) to meet SCA or other regulation requirements. However in future authentications on the same device, upon seeing an already-known BBK, the relying party can simply accept the SPC cryptogram as a full 2FA.
Note that the BBK is available cross-origin (and is unpartitioned), but the user has to have gone through two layers of consent - first the SPC transaction dialog, and then the WebAuthn authentication process. See "Potential privacy concerns" below.
API Shape
When API users want to create a passkey for payments they register via navigator.credential.create
setting the payment extension ("isPayment": true
), and now the public key is returned with the browser bound public key in clientDataJSON and the signature in the payment extension's outputs. E.g.,
credential = await navigator.credentials.create({
challenge: /* random Uint8Array generated by the server */,
...
extensions: {
"payment": {
isPayment: true,
// An optional list of allowed algorithms. When not present or empty, the
// pubKeyCredparams are used defaulting to ES256 and RS256. In this
// example ES256 and RS256 are allowed and RS256 is preferred.
browserBoundPubKeyCredParams: [
{
type: "public-key",
alg: -257 // "RS256"
},
{
type: "public-key",
alg: -7 // "ES256"
}
]
}
}
});
clientData = JSON.parse(arrayBufferToString(credential.response.clientDataJSON));
// browserBoundPublicKey is a base64url encoded - COSE_key representation of the public key
const bbkPublicKeyBase64UrlEncoded = clientData.payment.browserBoundPublicKey;
// The signature is an array buffer with a format specific to the algorithm.
const signatureArrayBuffer = credential.getClientExtenionResults().payment.browserBoundSignature.signature;
// The credential is sent to the server.
// Later the server verifies the cryptographic signature over clientDataJSON using browserBoundPublicKey.
When API users want to assert payments, they use the secure payment confirmation API (via PaymentRequest) and now the browser bound public key and signature are returned in the response, the public key in clientDataJSON and the signature in payment extension outputs (same as the registration above): E.g.,
const request = new PaymentRequest([{
supportedMethods: "secure-payment-confirmation",
data: {
// List of credential IDs obtained from the bank.
credentialIds,
rpId: "fancybank.example",
// The challenge is also obtained from the bank.
challenge: new Uint8Array([21,31,105 /* 29 more random bytes generated by the bank */]),
...
// An optional list of allowed algorithms defaulting to ES256 and RS256.
// In this example ES256 and RS256 are allowed and ES256 is preferred.
// Browser bound keys are not created when already present, so this
// list is only used when the browser bound key does need to be
// created.
browserBoundPubKeyCredParams: [
{
type: "public-key",
alg: -7 // "ES256"
},
{
type: "public-key",
alg: -257 // "RS256"
}
]
}], ...);
const response = await request.show();
await response.complete('success');
const credential = response.details;
clientData = JSON.parse(arrayBufferToString(credential.response.clientDataJSON));
// browserBoundPublicKey is a base64url encoded - COSE_key representation of the public key
const bbkPublicKeyBase64UrlEncoded = clientData.payment.browserBoundPublicKey;
// The signature is an array buffer with a format specific to the algorithm.
const signatureArrayBuffer = credential.getClientExtenionResults().payment.browserBoundSignature.signature;
// The credential is sent to the server.
// Later the server verifies the cryptographic signature over clientDataJSON using browserBoundPublicKey.
Device binding vs browser binding
BBKs would be not only device specific but also browser-specific, as each browser would have its own identifier for a given passkey. This would mean that a user might be stepped up for an additional challenge just by switching browsers on the same device (e.g., from Chrome to Edge) or even by switching user profiles on the same browser. This may increase friction, but it technically also provides a tighter binding and so may be seen as a feature rather than a bug!
Storage of the BBK
The two obvious locations for the BBK are either in the user agent's local database (software), or in a device TPM where present. The former might not be sufficient for payment industry threat models, so we may want to restrict this proposal to device TPMs only.
SPC is currently only allowed on devices with a user-verifying platform authenticator, which might imply the existence of a TPM. However it is not clear to the author whether or not that is always guaranteed, and we have plans to expand SPC to support roaming and hybrid authenticators in the future, so we probably cannot guarantee the existence of a TPM on the current device. As such, we will need to determine what behavior we want from the SPC API if a TPM is not available.
Potential privacy concerns
Storing and returning a BBK is a potential privacy risk. The main mitigations for this risk in the author's eyes are that the user has completed a WebAuthn authentication first and that they are reasonably provably in a 'payment' context (having accepted the SPC transaction dialog). WebAuthn credentials were historically device-bound and still may be on some platforms, so in theory this proposal is no more privacy sensitive than what already exists. But a full privacy analysis should be done, and user controls/etc should be considered to mitigate or minimize privacy risk.
FAQ / Open Questions
WebAuthn-less SPC
We have heard proposals in the WG for a fully WebAuthn-less version of SPC. That proposal is very similar to this one, in that the user agent creates a key on demand which is used to sign over the details. The WebAuthn-less proposal also did not require any user verification, just that the user clicked 'Continue' on the SPC transaction dialog.
Currently this proposal does not suggest moving away from WebAuthn. Although it is something the WG can and should discuss, there are two primary concerns:
- Having the trusted WebAuthn credential allows us to defeat person-in-the-middle attacks, because we can sign the BBK with the WebAuthn credential as part of ClientDataJSON, thus proving that it was definitely the browser that provided it.
- Removing WebAuthn increases the privacy risk to the user for being tracked. All a user has to do is click a 'Continue' button, and a BBK is given to the site. This may still be palatable/possible, but it seems worthwhile to keep it separate from this proposal.
What happens if/when WebAuthn launches DPK/SPK?
It is awkward that this proposal essentially competes with DPK/SPK, and raises the obvious question of what happens if/when WebAuthn launches DPK/SPK (on some or all platforms). Having both the SPC-specific key and DPK/SPK could even be a higher privacy risk, as you can identify groups of (different browser, same device), however one could do that with DPK/SPK and basic browser fingerprinting anyway.
Given that DPK/SPK seems unlikely to be ubiquitous across authenticators in any obvious timescale, it seems worthwhile to just keep the SPC-specific BBK even if/when DPK/SPK comes. We could encourage folks to use DPK/SPK instead, with a goal of eventually deprecating+removing the SPC-specific identifier, but it seems like a long and unlikely path.
An alternative (credit to Ian Jacobs!) might be to design the SPC-specific device browser binding API surface such that the browser could silently swap to WebAuthn's DPK/SPK support when available - that is, have the result be consistent whether the backing binding is WebAuthn or not. Investigation is needed here to determine how feasible it is, as matching WebAuthn's API shape may be infeasible.
Relation to Device Bound Session Credentials (DBSC)
Device bound session credentials do roughly the same thing as this proposal - give the ability to create a hardware-backed device identifier. The main impediments are again the time to launch (DBSC team plans to Origin Trial at earliest in late 2024), as well as increased complexity for payment partners - they would have to string calls to SPC and DBSC together and understand both technologies, rather than just calling SPC and having it 'just work'. Using DBSC might also require iframe-ing in the Relying Party, removing one of the key benefits of SPC.
Re-inventing the wheel
It is worth noting that this proposal is in some ways re-inventing the wheel of what already exists and/or will exist in WebAuthn. In particular, it means that we have to be careful to avoid all the traps/problems with signatures that WebAuthn already has solved (e.g., challenges to avoid replay attacks, choice of signing algorithms, quantum-proofing, etc). Where possible, we should look to write the spec relying on WebAuthn concepts, even if the actual key creation and storage does not use WebAuthn authenticators.
Device attestation
Along with device binding, we have also heard interest from the industry for attestation - that is, some way of proving that the storage/provider for the keys involved is a given known entity. The question of attestation is controversial, with both privacy as well as ecosystem impacts (e.g., it risks posing a barrier to entry for new browsers and/or platforms, if major players won't accept their keys as valid). At this time, this proposal deliberately does not include attestation of the browser or the underlying key storage system. The author wishes to explore if this device browser binding signal alone will suffice, without diving into the difficult world of attestation. At time of writing, the number of implementations of SPC is also small, and therefore attestation is of limited value.
Next Steps
As of time of writing, the next step is for the WG to discuss the proposal and determine if (a) it will suffice to meet the needs of the industry, and (b) if its adoption would accelerate/unlock usage of SPC among potential adopters. If the answer to both is reasonably close to 'yes', then there is non-trivial follow-on work needed to spec and build a prototype implementation of this for experimentation. The Chrome team is interested in tackling that this year, potentially (no promises!) in a Q3 timeframe for prototyping.