Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependence on Browser state for Primary Factor login #1356

Closed
ChadKillingsworth opened this issue Jan 4, 2020 · 12 comments
Closed

Dependence on Browser state for Primary Factor login #1356

ChadKillingsworth opened this issue Jan 4, 2020 · 12 comments

Comments

@ChadKillingsworth
Copy link

ChadKillingsworth commented Jan 4, 2020

I work in the financial industry and username enumeration is not permitted. That means to utilize webauthn for primary factor logins, the browser has to keep a state of whether or not the user has enabled it or not. This can be done via traditional storage mechanisms such as cookies, but I have found those to be fragile and frequently absent.

My concern is that a good portion of the anti-phishing benefit of webauthn for primary factor logins will be lost as a user will still think that a password based login flow is "normal" even though they have enabled webauthn for the primary factor.

For password credentials, the browser both stores and provides a UI for the user to select a credential to send to the server. In Chrome, it's possible to have a login without any HTML UI form elements. I'd propose a similar use case for PublicKeyCredentials. Allow the browser to prompt the user to select a username from the set of public key credentials stored on the device from which to initiate the ceremony. For this to be effective, the selection promise should immediately return with no UI if there are no available credentials (in the same way that navigator.credentials.get({password: true, mediation: 'required'}) behaves).

@nadalin
Copy link
Contributor

nadalin commented Jan 4, 2020

Why do you believe that the browser has to maintain state as to use webauthn or not ? Is there something you read?

@ChadKillingsworth
Copy link
Author

ChadKillingsworth commented Jan 5, 2020

Following the guidance of the spec to prevent username enumeration, our API will send back a valid PublicKeyCredentialRequestOptions response even for usernames which have not been enrolled. Calling navigator.credentials.get with those options where none of the allowedCredentials match still presents a UI. On Chrome the UI is an error and on Edge (not the Chromium version) it just asks the user to select any credential.

Screen Shot 2020-01-05 at 5 54 58 AM

Experimentation indicates that this UI triggers anytime there are zero matching credentials in the allowedCredentials array.

This UI flow prohibits me from beginning the webauthn ceremony for all users and requires that I know whether the user has been enrolled first. Our initial implementation uses a localstorage value to preserve state.

If username enumeration is allowed by the implementing system, this is not a problem. The API can simply indicate that the current user is not enrolled. That's simply not acceptable in my industry however.

@kreichgauer
Copy link
Contributor

Experimentation indicates that this UI triggers anytime there are zero matching credentials in the allowedCredentials array.

If this weren't the case, RPs would be able to probe silently for the (non-)existence of a credential with a given credential ID, which is a tracking concern.

If what you're interested in is login without a password, why would the user enter a username in the first place? Couldn't the site send a get() request with an empty allowList? The user would then select one of the resident credentials, and the site would be able to associate the correct user based on the response.

@ChadKillingsworth
Copy link
Author

If this weren't the case, RPs would be able to probe silently for the (non-)existence of a credential with a given credential ID, which is a tracking concern.

I was able to infer this restriction from the spec and appreciate it. Which is why I was attempting to suggest something slightly different.

Couldn't a UI prompt to select a registered user for the current site require a user gesture?

If what you're interested in is login without a password, why would the user enter a username in the first place? Couldn't the site send a get() request with an empty allowList? The user would then select one of the resident credentials, and the site would be able to associate the correct user based on the response.

Unfortunately no as this still leaves the user in an error state until at least one valid credential is registered. It's also a bit of a jarring user experience for selecting a user. For our flow, platform authenticators are an upgrade after a traditional username/password login experience.

@kreichgauer
Copy link
Contributor

Couldn't a UI prompt to select a registered user for the current site require a user gesture?

I'm guessing that would not be a very effective mitigation because users could be social-engineered into providing a sufficient number of user gestures ("keep holding down the space bar to win!").

@ChadKillingsworth
Copy link
Author

The UI flow we're using mirrors that of our native app exactly and provides a extremely pleasant user experience coupled with the anti-fishing benefits of webauthn in general. The only caveat is that state must be preserved on the client device.

This leaves my sites stuck between 2 security concerns: username enumeration or the possibility of tracking the user (but only on a site you had previously registered on right?).

We plan on releasing the current flow with the state persisted in local storage next week for an audience of 1.5 million monthly active users of multiple US banks and credit unions. I'd like to find a path forward though to remove the local state dependence so that the password based login flow is only the first time login experience.

@agl
Copy link
Contributor

agl commented Jan 15, 2020

Unfortunately no as this still leaves the user in an error state until at least one valid credential is registered. It's also a bit of a jarring user experience for selecting a user. For our flow, platform authenticators are an upgrade after a traditional username/password login experience.

I think you are hitting issues because you're aiming for something outside the intended flows for WebAuthn. There are two typical flows envisioned:

  1. The 2nd-factor case: user enters username + password. If correct, then a security-key challenge is an additional step and layer of security. Username enumeration is protected by needing to get the password right.
  2. The 1st-factor case: user clicks a button to trigger a request with an empty allowList. Account is selected from an authenticator on the client and information returned to the web site. No user enumeration because there's no username.

From the above, I'm wondering whether you're trying something like: user enters a username, hits enter, gets security-key challenge. Issues with that:

  1. It's single-factor: local proximity is sufficient. (Maybe that's ok.)
  2. In high-value situations, it lets with an unknown security key ask "does this security key belong to suspect $x?" up to the rate limit of the web site.
  3. It allows username enumeration, potentially.

The last point can be statistically ameliorated by having the server generate plausible, but random, credential IDs for any requested username by, e.g., HMACing the username with a server-side secret.

But, overall, that flow is not one that the WebAuthn design considers. The "1st-factor" flow is the intended answer there. Yep, it's possible that a user will click the button who hasn't actually enrolled a security key.

@ChadKillingsworth
Copy link
Author

If that's the case, then I expect adoption to suffer. I'll always have to present both login options simultaneously and a non-technical user will stick with what is familiar. The password based flow will be considered normal and we're back to the loss of phishing protection.

On native applications, it's typical for the app to know that the user has registered for biometric authentication (webauthn platform authenticators) and start the verification flow on the user's behalf automatically. I strongly feel that same flow should be the goal of webauthn primary factor - and in fact the spec does support it as long as the user agent knows whether to begin the ceremony for a particular username first.

In short - I think not considering this exact use case is a failing of the spec. Is there a way to add this use case?

@agl
Copy link
Contributor

agl commented Jan 22, 2020

(This was discussed on the call of 2020-01-22.)

A browser can't be sure whether a 1st-factor flow is suitable for a given website because users might be authenticating with, for example, a USB token that they insert.

In current designs, users register security keys as an additional layer of security and, after that point, they are required for that account to be authenticated, so fallback to passwords alone isn't permitted. (Some sites split the username entry out into a separate step so that they can better guide the authentication based on the exact account that's logging in.)

I get that you would like a button that triggers a WebAuthn login if it'll work, and immediately fails if not. But I don't believe that we can provide that given privacy concerns and given that the platform doesn't know what authenticators a user might insert.

Overall, the decision on the call was that we don't see an action here, I'm afraid.

@agl agl closed this as completed Jan 22, 2020
@ChadKillingsworth
Copy link
Author

Just to make sure we are talking about the same thing:

  1. I do split the username to a separate step to allow for a check for authn support first.
  2. I see usb keys and other cross platform authenticators as a 2nd factor authentication only.
  3. I use platform authenticators as a first factor authentication only after a traditional authentication with username, password followed by a 2nd factor auth of some sort has fully authenticated the user. Subsequent authentication can then use webauthn platform authentication as the the only factor.

The problem comes that I can't automatically test for the webauthn flow without some upfront knowledge. I could have a separate button on the username form and let the user decide, but I see that as requiring a huge amount of user education and I do not believe it will have substantial impact on phishing.

With these current restrictions I just don't see webauthn being usable for primary factor authentication unless username enumeration is permissible.

@ChadKillingsworth
Copy link
Author

It is entirely possible that I am utilizing the spec incorrectly, but there is very little guidance for this type of use and adoption is so low right now that there are very few patterns available from which to take inspiration.

@equalsJeffH
Copy link
Contributor

Our thinking has evolved in the year+ since this discussion -- sorry it took us so long; please see Explainer: WebAuthn Conditional/Hinted UI and WebAuthn PR #1576

nsatragno added a commit that referenced this issue Jun 29, 2022
Add conditional UI flow

This PR enables a non-modal "conditionally mediated" UI feature for WebAuthn which RPs may utilize to provide a credential selection UI only if the user has a discoverable credential registered with the Relying Party on their authenticator (the latter being the "condition"). The credential is displayed alongside an autofilled username or password input field. This helps RPs solve the "bootstrapping problem" when migrating their user base from traditional username and password to WebAuthn: websites can fire a WebAuthn call while showing their typical username and/or password prompt without worrying about showing a modal dialog error if the device lacks appropriate credentials.

See also:
https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-Conditional-UI
Fixes #1545

Overview "omnibus" issue: #1637
See also discussion in Issues #1356 #1533 #1568

Co-authored-by: Nina Satragno <nsatragno@gmail.com>
Co-authored-by: Jeff Hodges <nsatragno@gmail.com>
Co-authored-by: Emil Lundberg <emil@emlun.se>
Co-authored-by: Matthew Miller <mmiller@duosecurity.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants