Re: [webauthn] Review privacy concerns around error conditions (#2132)

## Enumeration of errors

The following errors may be thrown during [`create()`](https://w3c.github.io/webauthn/#sctn-createCredential) as of commit a871f796c591721c9556f119924ee29484b441f5 (2024-09-04):

During init:
- (If sameOriginWithAncestors is false) and (If options.mediation is present with the value conditional): Throw a "NotAllowedError" DOMException
- (If sameOriginWithAncestors is false) and (If the relevant global object, as determined by the calling create() implementation, does not have transient activation:): Throw a "NotAllowedError" DOMException
- (If the length of pkOptions.user.id is not between 1 and 64 bytes (inclusive)): throw a TypeError
- (If callerOrigin is an opaque origin): throw a "NotAllowedError" DOMException
- (If effective domain is not a valid domain): throw a "SecurityError" DOMException
- (If pkOptions.rp.id is present) and (If pkOptions.rp.id is not a registrable domain suffix of and is not equal to effectiveDomain) and (if the client supports related origin requests) and (Run the related origins validation procedure with arguments callerOrigin and rpIdRequested. If the result is false): throw a "SecurityError" DOMException
- (If pkOptions.rp.id is present) and (If pkOptions.rp.id is not a registrable domain suffix of and is not equal to effectiveDomain) and (if the client does not support related origin requests): throw a "SecurityError" DOMException
- (If pkOptions.pubKeyCredParams’s size is non-zero) and (after filtering pkOptions.pubKeyCredParams: If credTypesAndPubKeyAlgs is empty): throw a "NotSupportedError" DOMException
- (Extensions may throw unspecified errors)
- (If options.signal is present and aborted): throw the options.signal’s abort reason
- (If options.mediation is present with the value conditional) and (If the user agent has not recently mediated an authentication, the origin of said authentication is not callerOrigin, or the user does not consent to this type of credential creation): throw a "NotAllowedError" DOMException.

During lifetimeTimer wait loop:
- (If the user exercises a user agent user-interface option to cancel the process): throw a "NotAllowedError" DOMException
- (If options.signal is present and aborted): throw the options.signal’s abort reason
- (If pkOptions.authenticatorSelection.userVerification is set to required) and (If options.mediation is set to conditional and user verification cannot be collected during the ceremony): throw a ConstraintError DOMException
- (If any authenticator returns an error status equivalent to "InvalidStateError"): throw an "InvalidStateError" DOMException
- (If lifetimeTimer expires): throw a "NotAllowedError" DOMException


Rearranging this as a map of errors to causes of that error, and assigning numbers to each for easy reference:

- "ConstraintError" DOMException:
  - C1: During lifetimeTimer wait loop: (If pkOptions.authenticatorSelection.userVerification is set to required) and (If options.mediation is set to conditional and user verification cannot be collected during the ceremony)
- "InvalidStateError" DOMException:
  - I2: During lifetimeTimer wait loop: (If any authenticator returns an error status equivalent to "InvalidStateError")
- "NotAllowedError" DOMException:
  - NA3: During init: (If options.mediation is present with the value conditional) and (If the user agent has not recently mediated an authentication, the origin of said authentication is not callerOrigin, or the user does not consent to this type of credential creation)
  - NA4: During init: (If callerOrigin is an opaque origin)
  - NA5: During init: (If sameOriginWithAncestors is false) and (If options.mediation is present with the value conditional)
  - NA6: During init: (If sameOriginWithAncestors is false) and (If the relevant global object, as determined by the calling create() implementation, does not have transient activation:)
  - NA7: During lifetimeTimer wait loop: (If lifetimeTimer expires)
  - NA8: During lifetimeTimer wait loop: (If the user exercises a user agent user-interface option to cancel the process)
- "NotSupportedError" DOMException:
  - NS9: During init: (If pkOptions.pubKeyCredParams’s size is non-zero) and (after filtering pkOptions.pubKeyCredParams: If credTypesAndPubKeyAlgs is empty)
- "SecurityError" DOMException:
  - S10: During init: (If effective domain is not a valid domain)
  - S11: During init: (If pkOptions.rp.id is present) and (If pkOptions.rp.id is not a registrable domain suffix of and is not equal to effectiveDomain) and (if the client does not support related origin requests)
  - S12: During init: (If pkOptions.rp.id is present) and (If pkOptions.rp.id is not a registrable domain suffix of and is not equal to effectiveDomain) and (if the client supports related origin requests) and (Run the related origins validation procedure with arguments callerOrigin and rpIdRequested. If the result is false)
- TypeError:
  - T13: During init: (If the length of pkOptions.user.id is not between 1 and 64 bytes (inclusive))
- (Unspecified error):
  - X14: During init: (Extensions may throw unspecified errors)
  - X15: During init: (If options.signal is present and aborted)
  - X16: During lifetimeTimer wait loop: (If options.signal is present and aborted)


## Analysis

- **Concern 1:** (NA8) could cause an information leak that could identify the user without consent ([§14.5.1. Registration Ceremony Privacy](https://w3c.github.io/webauthn/#sctn-make-credential-privacy)). The information leak occurs if the Relying Party can distinguish between these cases:

  1. No authenticators are present.
  2. At least one authenticator is present, and at least one present authenticator is excluded.


  **Preconditions:**
  Case (ii) requires that the client has first probed the authenticator for any credentials listed in `excludeCredentials`.

  **Observations:**
  The only preconditions for (NA8) are that the client successfully runs through the initialization steps 1-22, then offers the user an option to cancel, and the user exercising that option. There is no precondition of probing for excluded credentials.

  **Conclusion:** (NA8) cannot cause an information leak that could identify the user without consent.

- **Concern 2:** (NA7) and (NA8) should be indistinghishable in order to not facilitate the information leak in Concern 1.

  **Observations:** (NA7) and (NA8) are already distinguishable, since (NA7) likely occurs around `options.publicKey.timeout` milliseconds after `create()` was invoked, especially if `options.publicKey.timeout` falls within the [recommended reasonable range](https://w3c.github.io/webauthn/#recommended-range-and-default-for-a-webauthn-ceremony-timeout).

  Since in Concern 1 we already concluded that (NA8) cannot cause the relevant information leak, the information leak cannot be made worse by (NA7) and (NA8) being distinguishable.

  **Conclusion:** (NA7) and (NA8) do not need to be indistinguishable. (NA7) can be safely changed to a distinct TimeoutError.

- **Remark 3**: (I2) _does_ allow the Relying Party to detect that an excluded credential is available to the user. However, this error has an explicit consent precondition in the authenticator operation: (If looking up descriptor.id in this authenticator returns non-null, and the returned item's RP ID and type match rpEntity.id and excludeCredentialDescriptorList.type respectively) and (If the user confirms consent to create a new credential). If the user does not consent to this, then the authenticator instead returns an error code equivalent to "NotAllowedError", which does not immediately cause an error on the client layer but most likely eventually results in (NA7) or (NA8) instead.




-- 
GitHub Notification of comment by emlun
Please view or discuss this issue at https://github.com/w3c/webauthn/issues/2132#issuecomment-2328586778 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 4 September 2024 11:00:14 UTC