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

Can't use Passkeys registered on devices with preferred authorization on devices without preferred authorization #281

Closed
Syfaro opened this issue Feb 19, 2023 · 15 comments · Fixed by #284

Comments

@Syfaro
Copy link

Syfaro commented Feb 19, 2023

Hi there, I'm running into an issue with using Passkeys between devices. Here's a quick way to reproduce it:

  • Use the default UserVerificationPolicy of Preferred
  • Register a Passkey on a device where preferred causes user verification to happen (such as an iPhone with Face ID)
  • Attempt to use the Passkey on a different device where preferred doesn't trigger user verification (such as a Mac Studio)
  • Get a UserNotVerified error

It seems like it's caused by this logic:

(_, UserVerificationPolicy::Preferred) => {
// If we asked for Preferred at registration, we MAY have established to the user
// that they are required to enter a pin, so we SHOULD enforce this.
if cred.user_verified && !data.authenticator_data.user_verified {
debug!("Token registered UV=preferred, enforcing UV policy.");
return Err(WebauthnError::UserNotVerified);
}
}

As far as I can tell, this causes the passkey to get "upgraded" into a state where it can only be used with user verification. However, it seems to be difficult to work around this behavior.

My current solution is enabling the "danger-credential-internals" feature, checking if the passkey Credential has user_verified set to true, and if so, setting the user verification of the RequestChallengeResponse to Required. Does this make sense, is there anything else I should be doing instead?

Thanks!

@Firstyear
Copy link
Member

Firstyear commented Feb 19, 2023

The reason this feature exists, is that without it uv=preferred can be arbitrarily removed and lowered to discouraged stripping uv from requests. Given that passkeys are meant to be a self contained MFA, the ability to strip UV is a significant security breach. This stripping can be done by js injection or userside scripting, allowing uv bypass since the content of the request to navigator.credentials.get is not hashed or reflected into the outputs the RP receives. This has been noted as CVE's in nextcloud, as well as receiving bugbounties/fixes from okta, azure and others. The reason why uv preferred is used at all over uv required is firefox, which has been lagging behind significantly in webauthn browser support for a long time. Were the situation different, our passkey wrapper would set uv=required.

In fact, this uv protection behaviour was created by this library and it was argued for a long time in the webauthn wg, until eventually consensus has reached that we should change the spec to guide implementations to do the same and protect and track uv states and apply that consistently ( w3c/webauthn#1774 )

With this in mind, what you have described here is not a flaw in this library, but a security flaw in Apple's passkey implementation. This library is just exposing it - it's not the first time that our very strict adherence to secure and correct behaviour has exposed issues in major platforms either.

However, we are unable to report this issue on your behalf - I wish I could! Apple's webauthn team are very closed off, and we have no consistent way to report issues. The best bet will be either through https://security.apple.com/ or through feedback assistant. In the feedback assistant case especially since you are the one in possession of the hardware, you can complete the report (since feedback assistant wants a hardware details upload, and mine is an m1 macbook pro that is not affected.) Of course, I'm happy to advise as much as possible, or be included in emails too.

The only outstanding questions I have is what happens if you do a request with UV required to the macstudio? When you create a credential with preferred / required on the macstudio, what are the UV states? What keyboard are you using, does it have touchid at all?

  • Create a Passkey on iPhone with UV=Required, then authenticate with the MacStudio (what happens?)
  • Create a Passkey on Macstudio with UV=Preferred - what UV state is listed in the created credential?
  • Create a Passkey on Macstudio with UV=Required - what UV state is listed in the created credential? Did it create at all?

I'm really sorry that this may not have been the answer you were looking for - on the other hand, well done finding a security flaw in Apple's passkey implementation!

--

If I was writing the report to Apple I think I'd draft text like:

User Verification bypass in iCloud passkeys.

When a credential is created on an iPhone and the relying party (RP) requests user verification (UV) preferred, the iPhone is able to satisfy this with TouchID/FaceID. This establishes to the RP and user that the credential in question is capable of self contained multifactor authentication (MFCDA, per nist 800-63b 5.1.9). When this credential is then synchronised to a Mac Studio with [List type of keyboard here, if it includes a touch id reader or not], then UV is silently discarded. In addition the user's machine password [IS or IS NOT] prompted for. The UV bit is then not set in attested credential data (ACD Webauthn L3 6.5.1 https://www.w3.org/TR/webauthn-3/#sctn-attested-credential-data ).

As a result, this creates a situation where a passkey that previously required local device authentication and UV, is able to bypass that requirement and use the credential without UV being set. This would constitute a UV bypass, and removes the guarantees of the authenticator from being MFCDA into a single factor cryptographic device authenticator.

The macstudio should prompt for and provide a source of UV, and then indicate this in the ACD flags to ensure that all passkeys in an iCloud account are consistent in their enforcement of UV and remains a MFCDA.

@Syfaro
Copy link
Author

Syfaro commented Feb 20, 2023

First off, thank you so much for the incredibly comprehensive response! I'm learning a lot here. I now see why this felt like really weird behavior, because it is! I'm developing some hobbyist software and browser compatibility isn't important, so I can set user verification to required to avoid this whole issue for now.

Here's the result of all of those situations. I'm using a Mac Studio without any kind of Touch ID capable keyboard.

Created on iPhone with AuthenticatorSelectionCriteria.user_verification set to Required:

  • iPhone prompts for Face ID.
  • The Passkey from finish_passkey_registration has registration_policy set to Preferred, user_verified is true.
    • Does this represent something weird happening? If userVerification is set to required, should the registration policy also be required? Is there something I can do to debug this further?
  • The Mac Studio doesn't ask for any kind of password or user verification.
  • The "Token registered UV=preferred, enforcing UV policy." message is logged and the "The user verified bit is not set, and required by policy" error is generated.

Created on Mac Studio with the default values (should be user verification preferred):

  • Mac Studio does no user verification prompt, just a Continue button.
  • The Passkey has registration_policy set to Preferred, user_verified is false.
  • No user verification is prompted and can successfully authorize with the Continue button.

Created on the Mac Studio with user verification set to required:

  • Mac Studio prompts for password.
  • Same as with the iPhone, registration policy is set to preferred and user verified is true.
  • Attempting to use the passkey for future authorizations results in the same failure as the passkey created on the iPhone.

In case it's relevant, all of these tests were done in Safari and I'm running macOS 13.2.1, my iPhone is on iOS 16.4, and my iPad which exhibits the same behavior as the iPhone is on 16.3.1.

Another data point, created on Mac Studio in Chrome with default preferred or required:

  • Mac Studio asks for user verification for both registration and authentication via Apple Watch or password.
  • Passkey has registration_policy always set to preferred, user verified is always true.
  • Authentication works as expected.

I'll go ahead and close this issue as it does appear to be caused by something else, but let me know if there's anything else I can do!

@Syfaro Syfaro closed this as completed Feb 20, 2023
@Firstyear
Copy link
Member

First off, thank you so much for the incredibly comprehensive response! I'm learning a lot here. I now see why this felt like really weird behavior, because it is! I'm developing some hobbyist software and browser compatibility isn't important, so I can set user verification to required to avoid this whole issue for now.

Sounds like a plan! I'm glad that I was able to help.

Here's the result of all of those situations. I'm using a Mac Studio without any kind of Touch ID capable keyboard.

That's possibly part of the issue is my guess.

Created on iPhone with AuthenticatorSelectionCriteria.user_verification set to Required:

  • iPhone prompts for Face ID.

  • The Passkey from finish_passkey_registration has registration_policy set to Preferred, user_verified is true.

    • Does this represent something weird happening? If userVerification is set to required, should the registration policy also be required? Is there something I can do to debug this further?

Yeah, this does seem weird. i'm going to assume you set this in the call to generate_challenge_register_options()? Or did you alter this in the request that was given to navigator.credentials.get? Inside the library the policy is passed down directly from register_credential_internal into Credential::new, so it should be an exact reflection of what was request from the generate_challenge_register_options.

Created on Mac Studio with the default values (should be user verification preferred):

  • Mac Studio does no user verification prompt, just a Continue button.
  • The Passkey has registration_policy set to Preferred, user_verified is false.
  • No user verification is prompted and can successfully authorize with the Continue button.

Well at least it's consistently wrong 🤣

Created on the Mac Studio with user verification set to required:

  • Mac Studio prompts for password.
  • Same as with the iPhone, registration policy is set to preferred and user verified is true.
  • Attempting to use the passkey for future authorizations results in the same failure as the passkey created on the iPhone.

It does sound like there is an odd policy pass through issue here though. I'll have a look today. Are you doing this with your own site or our example?

But also good to know that if you set required, the UV can be asserted via the password prompt. The question is why apple doesn't do this with UV=preferred since it weakens the security of the passkey as a whole.

  • Mac Studio asks for user verification for both registration and authentication via Apple Watch or password.
  • Passkey has registration_policy always set to preferred, user verified is always true.
  • Authentication works as expected.

Yep, chrome doesn't use the normal icloud passkeys which is why it probably "does the right thing".

I'll go ahead and close this issue as it does appear to be caused by something else, but let me know if there's anything else I can do!

As mentioned, it would be great to report this as a security issue to apple :)

@Firstyear
Copy link
Member

Followup - Just tested and I can't reproduce the policy pass through quirk you are seeing, I'm seeing the uv policy pass through correctly to the credential. So I would like to know more about how you are setting or modifying this value.

@Syfaro
Copy link
Author

Syfaro commented Feb 20, 2023

i'm going to assume you set this in the call to generate_challenge_register_options()? Or did you alter this in the request that was given to navigator.credentials.get? Inside the library the policy is passed down directly from register_credential_internal into Credential::new, so it should be an exact reflection of what was request from the generate_challenge_register_options.

Ah, I think this was a case of me using it wrong then. It looks like there's no way to set it to be required through the public API of the webauthn-rs crate, generate_challenge_register_options requires having access to the WebauthnCore which isn't public and using the core crate directly for everything is a little too scary! I had been updating the authenticator selection criteria of the creation challenge response which I now see is only changing the part that goes to the client, and the registration state of the passkey registration isn't public so I can't modify it the same way.

As mentioned, it would be great to report this as a security issue to apple :)

I'll have to give that a try.

@Firstyear
Copy link
Member

Ahh yep, that would do it! Thanks for explaining :)

Good luck with webauthn-rs in your project, if you have any other questions or feedback please let us know!

@Firstyear
Copy link
Member

@Syfaro Hi there, thanks to a conversation with another project contributor we were able to reproduce this on a macbook pro. I'll go ahead and report it to apple as well :)

@Syfaro
Copy link
Author

Syfaro commented Feb 21, 2023

Oh interesting, was it exhibiting the behavior when in clamshell mode?

I'd be curious if you can reproduce another issue I was running into, when using autofill it seems to ignore required user verification.

@Firstyear
Copy link
Member

Do you mean conditional mediation for your autofill?

@Syfaro
Copy link
Author

Syfaro commented Feb 21, 2023

Yup, I do mean conditional mediation.

@Firstyear
Copy link
Member

Okay, can I get some more details? When you say required user verification do you mean that you have set uv=required in the library code? Or is it still set to preferred? In addition this is still with the macstudio and the touchid-less keyboard?

@Syfaro
Copy link
Author

Syfaro commented Feb 21, 2023

I'm setting user verification required in the library code which I can see is being passed to the client, and yes this is still on the Mac Studio with no Touch ID capable keyboard. When I select the passkey it immediately submits without performing user verification. It works as expected on a MacBook with Touch ID available.

@Firstyear
Copy link
Member

I just tried with UV required with the macbook pro in clamshell mode and no touchID, and in both cases with conditional UI I was prompted for password to register/authenticate. So either it's something different in your setup conditions, or it is something macstudio unique? What site are you using for the conditional UI test?

@Syfaro
Copy link
Author

Syfaro commented Feb 22, 2023

I've tried on my own site and the tester here: https://webauthn.firstyear.id.au/condui_test

I get a couple of errors on the tester site (AttestationNotVerifiable when trying to register, InvalidUserUniqueId when trying to authenticate), but you can see I'm not prompted for anything when clicking the passkey from the autofill entry.

2023-02-21 19 20 35

@Firstyear
Copy link
Member

Yeah, those errors aren't a problem in this context - the passkey is still created and them attempts to be used. I think that's an older version of the tester though, I'll update it shortly.

Saying this, you're absolutely correct, there certainly does appear to be some kind of issue present as you are identifying. I'm just not sure how to reproduce it myself at this point.

In general, this is why we have hidden conditional ui prompts behind flags in the library - every browser has scuffed their implementation in some way, the worst being one browser that outright crashes the whole process when you access a conditional ui prompt. At this point I'm still not convinced it's a good user experience yet given all the complexity to checks notes auto complete a username (especially when autofill/complete already exists ....).

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

Successfully merging a pull request may close this issue.

2 participants