duo-labs / webauthn

WebAuthn (FIDO2) server library written in Go
https://webauthn.io/
BSD 3-Clause "New" or "Revised" License
1.03k stars 162 forks source link

RPOrigin is validated exactly against the url's fully qualified host name leaving no room for other use cases. #143

Closed igmor closed 1 year ago

igmor commented 2 years ago

There's a use case where a user performs the WebAuthn registration or verification at sub.domain.com, some portal page, then there are some redirects until the resultant WebAuthn data hits auth.domain.com. This is where the actual RP verification happens, and at this point, the origin validation would fail, because the RP expects an origin of auth.domain.com from the request context, whereas the actual WebAuthn request was performed at sub.domain.com.

Here is how it is implemented: https://github.com/duo-labs/webauthn/blob/4d1cf2d3405194e18bbcaa654485927e9a985707/protocol/client.go#L93

Another use case might be some FIDO2 SAMLv2 proxy, where a single entity would authenticate users via webauthn protocol and then would redirect a request to a saml validation endpoint.

Here is how it is implemented in alternative libraries, note there is a list of preconfigured RP Origins

https://github.com/webauthn4j/webauthn4j/blob/b3f67afb94dc4187b7b01b4e593c8560841f27c0/webauthn4j-core/src/main/java/com/webauthn4j/validator/OriginValidatorImpl.java#L56

maxwelmunthe27 commented 2 years ago

I got the same issue too

arianvp commented 2 years ago

Would it be possible for you to set your RPID to domain.com when initating the flow on auth.domain.com ? then sub.domain.com will be able to verify the assertion just fine as sub.domain.com is a valid origin for the domain.com rpId

By default, the RP ID for a WebAuthn operation is set to the caller’s origin's effective domain. This default MAY be overridden by the caller, as long as the caller-specified RP ID value is a registrable domain suffix of or is equal to the caller’s origin's effective domain.

Note: An RP ID is based on a host's domain name. It does not itself include a scheme or port, as an origin does. The RP ID of a public key credential determines its scope. I.e., it determines the set of origins on which the public key credential may be exercised, as follows: The RP ID must be equal to the origin's effective domain, or a registrable domain suffix of the origin's effective domain. The origin's scheme must be https. The origin's port is unrestricted. For example, given a Relying Party whose origin is https://login.example.com:1337, then the following RP IDs are valid: login.example.com (default) and example.com, but not m.login.example.com and not com.

This is done in order to match the behavior of pervasively deployed ambient credentials (e.g., cookies, [RFC6265]). Please note that this is a greater relaxation of "same-origin" restrictions than what document.domain's setter provides.

These restrictions on origin values apply to WebAuthn Clients.

Basically if you set the rpId manually in your navigator.credentials.get call like this :

{ rp : { id : "domain.com" }}

Then the clientDataOrigin will report domain.com instead of auth.domain.com

If you then set relyingPartyOrigin to domain.com too the check will succeed.

arianvp commented 2 years ago

webauthn-rs also takes a list of origins. So it might be indeed the more ergonomic API to adapt:

https://github.com/kanidm/webauthn-rs/issues/193

MasterKale commented 2 years ago

Support for multiple origins is also in the two libraries I wrote:

It's a common enough scenario in more complex RPs that I think this library should implement similar functionality.

igmor commented 2 years ago

yeah scoping roprigin to a general domain would work for some use cases. I was also looking into supporting the list of unrelated domains.