w3c / secure-payment-confirmation

Secure Payment Confirmation (SPC)
https://w3c.github.io/secure-payment-confirmation/
Other
114 stars 40 forks source link

Encrypt SPC Credential Identifiers in authentication flows to increase privacy #77

Closed ianbjacobs closed 2 years ago

ianbjacobs commented 3 years ago

@Goosth and I believe others have pointed out that it would be great to reduce tracking risk by not reusing the same SPC Credential IDs across transactions. Here is an idea:

1) At enrollment, the relying party provides a public key in addition to other data (e.g., display information, relying party ID). The relying party associates this public key with the SPC Credential Identifier they get back through enrollment.

2) At transaction time, the relying party prepares input for SPC (either for someone like a PSP or their own usage) by encrypting a hash of an SPC Credential ID along with a time stamp (for example). The relying party ID and encrypted blob are provided as input to SPC.

3) The browser follows this algorithm (or similar):

This is a rough idea; I look forward to hearing suggestions to make it realistic and effective.

rsolomakhin commented 3 years ago

Which algorithm would be used for encryption in this proposal, @ianbjacobs?

Alternative idea is to hash the credential ID with a salt. The salt can be either random or based on the merchant origin.

Random salt:

  1. Merchant requests the list of credential IDs from the issuer.
  2. Issuer generates a random salt.
  3. Issuser sends [salt, hash(salt || credential ID] to the merchant. SHA256 or SHA512 can be used for hashing.
  4. Merchant invokes PaymentRequest API with this hashed credential ID and the salt.
  5. The browser generates hash(salt || credential ID) for each of the credential IDs that it has stored in the user profile.
  6. If any of the hashes match what the merchant provided, then show the verification UI to the user.
  7. Include the salt in the signature to further reduce the risk of replay attacks.

Merchant based salt

For merchant origin based salt, the issuer does not need to generate random salt in step 2. Instead, both issuer and browser use the origin of the merchant as the salt. The salt would not need to be included in the signature, because the signature already included the merchant origin.

A couple of questions:

  1. What other ways do we have to hide the credential ID from the merchant?
  2. How important is this?
stephenmcgruer commented 3 years ago
  1. What other ways do we have to hide the credential ID from the merchant?

One (still not very thought-through) option that we've discussed is letting the relying party provide a fetch URL to get the credentials from, rather than have the caller provide the credentials from. The happy path is then that the merchant gets the URL (from their PSP, or 3DS flow, whatever), and passes it to SPC. The browser fetches from the URL, and uses those credentials.

Of course, this is ripe for abuse if the merchant just fetches the URL itself. A relying party could have some protection by making the URL a one-time URL, which allows for detecting if not prevention, but it does get messy...

An advantage of the URL approach vs the salt approach is that it doesn't require the browser to store a list of credential IDs in the user profile, which I consider an annoying part of SPC currently and would love to get rid of! (Because it significantly inhibits portability.)

ianbjacobs commented 3 years ago

Thanks @rsolomakhin and @stephenmcgruer for the rich ideas. One downside of the URL approach is that RPs need to then offer public-facing services.

@rsolomakhin, the merchant salt idea sounds interesting. I assume that it relies on the relying party never sharing the "real" SPC Credential ID. Is that something we should rely on? (Perhaps the risk is no greater than the RP sharing the private key that is featured in my pseudo-algorithm.)

Regarding "how important is this" I don't have a good sense. It is probably more valuable in payment methods where the instrument data itself is well-managed (e.g., tokenized, origin-bound card info).

For the moment I've described this as "should" in the requirements document. I assume through discussion we will find out whether this feature is a "must".

ianbjacobs commented 3 years ago

Side note: if the credentials change every transaction, one implication is that caching credentials will not work. I don't yet know how desirable or common it would be to cache credentials.

Goosth commented 2 years ago

@ianbjacobs, @stephenmcgruer & @rsolomakhin

This post will explore how we could utilize Public-private keypairs (asymmetric encryption) to provide a layer of obscurity and anonymization to protect Credential ID's for SPC.

There are 4 entities participating in an SPC journey

The RP (bank) has the identifier (PAN) to credential mapping. We do not want the intermediary to know anything they do not need (for various reasons, including privacy and potential caching of credential ids). The Browser is implicitly trusted and needs to know the credentials, since it needs to call the Fido Authenticator.

Transporting the information What if we could let the RP pass a JSON Web Encryption (JWE at https://datatracker.ietf.org/doc/html/rfc7516) packet to the Browser? This JWE will be passed to the browser via the Intermediary, but the intermediary would not be able to see or change any value.

The JWE could contain the following encrypted fields

The Browser needs to be able to verify and decrypt the JWE, since it needs to use it in the SPC display; to reach out to the correct the Fido Authenticators and also to sign the result.

Transporting the key material If the Browser Agent could generate a keypair (with the private key kept secret) for each SPC session, and the public key is passed to the RP, the RP would be able to generate the JWE and the browser would be able to decrypt it. (Implementation note: We would be able to use WebCrypto API’s, which would not required additional crypto-work inside the Browser)?

The intermediary already has a back-channel communications mechanism to the Bank (e.g. 3D Secure). As part of this, it today already passes along the PAN, for example, and in return receives a list of credentials and instrument details.

For this to work, the Intermediary would have to do two things:

The RP would also need to do some more work to generate this JWE, but we can make that an RP decision, and not enforce it (this optionality needs some thinking, since it could make Intermediary implementations more complex)

Summary Summary of the flow would therefore be

Would something like this make sense? If so, how do we unpack/design this in more detail?

stephenmcgruer commented 2 years ago

This was discussed today at the SPC task-force. We do not believe that the proposed JWT approach would work, as the merchant can trivially person-in-the-middle it as follows:

  1. The Merchant asks the browser for an SPC public key: Browser generates (browser_pub, browser_priv)
  2. The Merchant generates their own public/private key-pair: Merchant generates (merchant_pub, merchant_priv)
  3. The Merchant sends merchant_pub via the back-channel communication to the Relying Party.
  4. The Relying Party encrypts the JWT with merchant_pub and sends it back to the Merchant.
  5. The Merchant decrypts the JWT with merchant_priv, and reads the decrypted credential data.
  6. The Merchant encrypts the JWT with browser_pub, and passes it into SPC.
  7. SPC proceeds as normal.

In general, an encryption based scheme can only work if there is some alternative communication channel directly between the Browser and the Relying Party. This could be online (e.g. during SPC) or offline (e.g. by browsers publishing some known public key that the Relying Party can use), but in either case substantially complicates matters.

I think, but have not verified, that the salting proposal is not subject to a person-in-the-middle attack, but it also may not be compatible with FIDO at all - we've asked the FIDO/WebAuthn folks to take a look.

ve7jtb commented 2 years ago

The salt idea would only work for "Discoverable" credentials. The platform would need to authenticate the user to get the list of credentials from the credential management API then do the hashing with the salt to compare.

Non-resident credentials encode information in the credentialID itself so they would be broken by this.

The other thing to consider is that the authentication response contains the credentialID in the publickeycredentialdescriptor. https://w3c.github.io/webauthn/#dictdef-publickeycredentialdescriptor

Encrypting or hashing the request is not sufficient, to protect the information from intermediate parties we would also need to encrypt the response to the RP. That could be done by having the issuer store a key in credblob and have the browser use that to encrypt the response. That is however a lot of additional complexity.

cyberphone commented 2 years ago

@ve7jtb Since SPC anyway requires you to handover your credit card number to the merchant (https://github.com/w3ctag/design-reviews/issues/675#issuecomment-964273692), I don't see much of a rationale for protecting credentialId from a privacy perspective. BTW, Apple Pay, Google Pay, and most similar solutions never expose key handles outside of the payment application.

npdoty commented 2 years ago

suggestion on today's call was that one non-technical mitigation was contractual limits on merchants retaining credential IDs

ianbjacobs commented 2 years ago

Based on today's discussion, the Editors will work on additional text for the specification regarding guidance to RPs.

ianbjacobs commented 2 years ago

Closing this after merger of pull request #189. Thank you @samuelweiler and @npdoty! (And thank you for updating the privacy labels as needed.)