w3c / webauthn

Web Authentication: An API for accessing Public Key Credentials
https://w3c.github.io/webauthn/
Other
1.16k stars 167 forks source link

Why not make things simple? #1709

Closed dagnelies closed 2 years ago

dagnelies commented 2 years ago

Hi,

RFC was originally meant as a "Request For Comments", so here goes my comment as a humble nobody.

I find the goal of the spec revolutionary and it is an ideal to reach. Less passwords, more security, smoother interaction. That's what the world needs!

However, regarding the spec itself, I've never seen (nor could I have imagined) something so complex, messy and feeling so outdated.

Since the goal itself is amazing and to "make it work so far" is already a tremendous achievement, I'm also sorry to hurt some feelings here. However, from a third-party's perspective, the spec in its current form is a source of frustration and headaches. I would even argue that its own complexity hampers adoption and increases security risks, since proper validation and ensuring that no loopholes were missed is very tricky in this 165 pages long spec.

What also puzzles me greatly is that there are already wonderful and simple RFCs for signing content. It's nothing new, it's something really basic and widespread. I'm referring to JWT which is simply arbitrary JSON payload with a signature. It would have been so simple if this API used it. The whole specs would have been simplified greatly:

let registration = await navigator.credentials.create({...}); // Obtain a JSON Web Token
let login        = await navigator.credentials.get({...});    // Obtain a JSON Web Token

Here goes your signed content. Everyone understands it, it's simple, it's standardized, there is an RFC for it, and a plethora of tested libs to validate their signatures. It would have been so easy. ...so well, if there is a new version one day, it would be nice to see this RFC cut down to a few simple pages, with tokens that are easily validated and understood. I guess it's a pipe dream, but who knows, asking never hurts ...I hope.

Please bear in mind that this RFC is shaping the future of passwordless, it's no small feat! Let me stress the importance and far reaching consequences of it. Being smaller, clearer, easier to work with would provide profound and significant impact. It would make the adoption faster, easier, safer ...or the opposite for the decades to come.

Thanks for your attention, sorry for the hurt feelings.

yackermann commented 2 years ago

Hey @dagnelies

So first, I actually written a whole series of article just about FIDO2 and Webauthn: https://medium.com/webauthnworks/webauthn-fido-series-content-page-4f9a187aa588

And there are a good list of existing solutions: https://github.com/herrjemand/awesome-webauthn#server-libs

Next let me break down your whys:

outdated: byte buffers containing CBOR structures, which in turn contain nested byte buffers which are a concatenation of flags, byte lengths and nested CBOR... shouldn't such things a relic of the last century?!

I am not sure why are you referring to CBOR as outdates since it's a quiet new standard. The reason why it is like that is because the authenticator, like a physical security key, Yubikey, Trustkey, is communicating using CTAP2 protocol which in it self uses CBOR as efficient standard for encoding and decoding structs.

messy: there are many different attestation formats, with each its own way to be parsed, interpreted and validated... Isn't it kind of ironic for a "standard"?!

There are different kinds of devices, with different kinds of hardware, with different kinds of manufacturers, and different kinds of specifics of how device is attested, and what is is even attested. iOS does it differently, to Windows TPM, to Android Safetynet.

In general no one really even needs attestation. This is mostly enterprise feature. You can read this article to learn more: https://medium.com/webauthnworks/webauthn-fido2-demystifying-attestation-and-mds-efc3b3cb3651

complex: it's fucking 165 pages long!!! It's huge, it's difficult to read, it's sometimes unclear, sometimes obscure... Who can even understand all that?!

Webauthn, same as every standard, grows to satisfy industry needs. Same as every other standard in requires some pre-requisite knowledge. There are workshops, and tutorials you can start with. Diving into the standard as a newcomer might be tough. Take a look at that: https://medium.com/webauthnworks/introduction-to-webauthn-api-5fd1fb46c285

And please avoid swearing.

For the rest of the question, I might just add that even though the webauthn is not perfect, because nothing is, this standard is five years of work, debates, arguments, and careful consideration of many specialists, including the creators of JWT if you are not aware, and this was what they came up with.

If you have some clear, and constructive ideas, that would be far more beneficial to this group, than coming out with vocal IMHO "everything is crap, I don't like it".

dagnelies commented 2 years ago

Thank you for your enlightening and calm arguments despite my ranting tone, which was a bit rude.

Firstyear commented 2 years ago

In general no one really even needs attestation. This is mostly enterprise feature. You can read this article to learn more: https://medium.com/webauthnworks/webauthn-fido2-demystifying-attestation-and-mds-efc3b3cb3651

The problem though is almost nothing about an authenticator can be trusted outside of "It gave me a signature" without attestation though. (The standard also does not explicitly say this either .... )

Webauthn, same as every standard, grows to satisfy industry needs. Same as every other standard in requires some pre-requisite knowledge.

But this is a legitimate frustration (swearing aside). Webauthn as a spec exists to communicate to an audience how to implement a set of routines. If it's failing to communicate clearly to the target market, then as a standard group we are failing in our mission. And even worse, failing to communicate will (and has) led to incorrect and insecure implementations (to date, I've seen insecure nonce in RP's, UV bypass, invalid attestations from a mobile device manufacturer, and probably more.)

I implemented webauthn for rust and I find the standard dense and hard to parse. I can empathise that anyone outside would have a really difficult time. There are some huge barriers in this document that should be addressed.

dagnelies commented 2 years ago

Just for the sake of completeness, I would like to emphasize some current hurdles from an outsiders perspective.

For example, the objects you get from credentials.create are...

  1. binary, it's not human readable, making it harder to understand and debug
  2. it uses an exotic binary format requiring external libs to decode it
  3. you have to re-encode in json anyway to send it over the wire
  4. the format itself is complex and varies depending on the authenticator
  5. it doesn't use other well known RFCs like JWT for signing content

Instead of dealing with the complex formats and diversity at the source, the millions of "relying parties" using it have to deal with it ...hoping they do it right. It would have been nice if either the Browsers/Authenticators would take care of a "standardized" signature and simple readable json metadata. I guess there are some things that "cannot be changed" because it is outside of your control, resulting in difficult compromises. Nevertheless, I think one big step forward would be if this spec ensures the results are homogenized, simple and human readable.

emlun commented 2 years ago

I appreciate the frustration, and I invite you and everyone else to hurt our feelings [edit: what I meant to say is we should not confuse critique of the spec for criticism of our persons, but we should all of course abide by the W3C code of conduct] as much as is needed to make the spec the best it can be. I agree with @Firstyear that if the spec fails to communicate clearly to its audience, that is primarily the fault of the spec. However, as a spec intended for wide interoperability we may need to prioritize impartial generality over concrete examples, or sacrifice brevity to avoid ambiguity, or on the flip side sacrifice detailed descriptions for conciseness. Where the spec is vague it is often intentional so as not to restrict implementations or use cases too much.

I can't speak for every technical decision in the spec as I wasn't around for its early development, but I'll try to add some colour to some of your grievances:

  • outdated: byte buffers containing CBOR structures, which in turn contain nested byte buffers which are a concatenation of flags, byte lengths and nested CBOR... shouldn't such things a relic of the last century?!

WebAuthn is in large part designed to be compatible with the FIDO CTAP protocol, which uses CBOR for many things including signatures. I don't know why CBOR specifically was chosen for that, though. But as for the raw byte concatenations, again that is in part motivated by being backwards compatible with U2F signatures so that millions of existing U2F tokens (aka "Security Keys" (SKs)) can be used with WebAuthn. I suppose this also largely precludes JWT/JWS as a "signature format", since we cannot change the way U2F tokens assemble the data to be signed.

As an RP library implementer myself, though, I think CBOR does have some merits of its own. It's largely a binary analogue of JSON, which makes direct translation between the two somewhat straightforward apart from byte array values. For example, the Jackson library for Java supports both JSON and CBOR as (de)serialization backends using the same logical data structures. Similarly, the COSE key data structures are (mostly) direct analogues of JOSE which JWT/JWS builds on.

The use of byte buffer types rather than, say, base64url-encoded strings in results was understood to reflect W3C direction for JavaScript APIs. There is currently some movement towards some "JSON-ized" API in PR #1703, although nothing is quite certain yet.

  • messy: there are many different attestation formats, with each its own way to be parsed, interpreted and validated... Isn't it kind of ironic for a "standard"?!

Indeed attestation and its many nuances is probably the most complex part of the spec, and as @herrjemand also points out it's messy because the outside world is messy. But indeed most RPs won't actually need the guarantees that attestation can provide, so they don't really need to implement all that complexity.

Hm, with that in mind... perhaps we should split §7.1. Registering a New Credential into two versions: a greatly simplified one for RPs that don't need attestation, and the current one for RPs that do.

  • complex: it's fucking 165 pages long!!! It's huge, it's difficult to read, it's sometimes unclear, sometimes obscure... Who can even understand all that?!

If it's any consolation, keep in mind that the spec has at least three audiences: RP implementers, client (browser) implementers, and authenticator implementers. For example, RP implementers don't need to read sections §5.1.3 - §5.1.7 as those are mostly about the internals of how browsers are to implement the operations. That is unfortunately less the case in L1 and L2, as there was little guidance about what various parameters actually mean to an RP outside of reading the implementation details of what clients are to do with them, but at least for L3 we've made an effort to be more clear about what the parameters mean from an RP perspective and what the client and authenticator will do with them.

But of course there's always more we can do to be better. If you can point us to specific parts or concepts that are unclear, or hard to read or understand, we'll be happy to do our best to improve them. Just be prepared that not everything is a good fit to include in the spec itself - it is indeed already very long. Some things may fit better in external resources such as those @herrjemand mentioned.

Firstyear commented 2 years ago

I appreciate the frustration, and I invite you and everyone else to hurt our feelings as much as is needed to make the spec the best it can be. I agree with @Firstyear that if the spec fails to communicate clearly to its audience, that is primarily the fault of the spec. However, as a spec intended for wide interoperability we may need to prioritize impartial generality over concrete examples, or sacrifice brevity to avoid ambiguity, or on the flip side sacrifice detailed descriptions for conciseness. Where the spec is vague it is often intentional so as not to restrict implementations or use cases too much.

An example at the moment is over in the "backup" bit thread, that if it's a ux hint, it should have "hint" in the name. We choose language in the spec that implies absolutes and strict states, but when we mean "this is a hint and a guide or similar" but we don't choose to express that.

Another good example is that the structs in webauthn don't delineate what is signed and what is not, which gives people a false sense of "security" unless they really know the internals of webauthn inside and out (and even then, it seems many of these issues were missed ...).

Finally, things like authenticator selection criteria really focuses webauthn that an authentication challenge is targeted by an RP to a single or very narrow band of credential classes, but the spec isn't clear that this kind of mix-match can create security issues related to UV bypass, and generally pushes a lot of burden to the RP that doesn't really need to be there.

I'm glad for the json base64 changes, but I also did previously raise it and it was closed sadly too.

Anyway, sadly, the ship has probably sailed on most of these unless we are willing to do a fully breaking change for a level 4 version of the spec.

dagnelies commented 2 years ago

In general no one really even needs attestation. This is mostly enterprise feature. You can read this article to learn more: https://medium.com/webauthnworks/webauthn-fido2-demystifying-attestation-and-mds-efc3b3cb3651

The problem though is almost nothing about an authenticator can be trusted outside of "It gave me a signature" without attestation though. (The standard also does not explicitly say this either .... )

Indeed attestation and its many nuances is probably the most complex part of the spec, and as @herrjemand also points out it's messy because the outside world is messy. But indeed most RPs won't actually need the guarantees that attestation can provide, so they don't really need to implement all that complexity.

As far as I understand, when registering credentials, the signature is concealed somewhere deep in this authenticator-specific attStmt object.

So, without attestation means without signature and the client can freely tamper with all the data anyway (very easely). In the end, in the absence of attestation/signature, you can simply send the public key over since none of the data can be trusted anyway.

Or did I miss something?

Providing a signature field like in credentials.get would have been nice.

emlun commented 2 years ago

@dagnelies Yes, you are correct that without a signature during registration, the response can be freely tampered with. However, that is still mostly true even with attestation, because that's not a problem attestation is meant to solve. Copying my response from #1710 for the benefit of other readers:

Yeah, the critical piece to realize is that all WebAuthn credentials are "trust on first use" (TOFU) keys. Even with attestation, it is indeed possible for a malicious script to replace the public key and attestation statement during registration, and thus have the attacker's key registered instead of the victim's key. We touch on this in 13.4.4. Attestation Limitations in the Security Considerations section.

What attestation does is provide a way for the authenticator to prove to the RP what kind of authenticator it is. That proof could eliminate the possibility of software-generated malicious keys, but a malicious script could still in theory call out to a remote server to generate an attestation statement using a genuine authenticator on the RP's allow list. At best the RP could display a name and/or image of the authenticator as identified by the attestation statement, which could allow attentive users to notice if there's a discrepancy. But even then the attacker's remote server could have an array of authenticators of different brands and models to account for that too. And of course, self attestation is always susceptible to tampering since by definition there's no way to link the attestation key to any established root of trust.

So yeah, the registration ceremony is a leap of faith, with or without attestation. But assuming one was completed securely, any subsequent authentication ceremonies are highly resistant to tampering since at that point there is an established trust relationship with a particular credential public key. You cannot know with certainty who the public key came from during registration, but you can know with certainty that an authentication signature came from the same key pair as was registered.

As for your last remark:

Providing a signature field like in credentials.get would have been nice.

Without previous knowledge of the public key for that signature, the signature will not prevent tampering - it will at most prevent accidental data corruption. The registration ceremony is similar to a "key signing party": you have no previous trust relationship with the user, so any signature they could give you at that point is meaningless, but once you have their public key you can securely authenticate them after that. The attestation signature just gives you more things you can assume (like whether private keys can be copied) after you have established that trust relationship.

emlun commented 2 years ago

Given the lack of activity it seems like there's not much more to discuss here, so we're closing this. You're welcome to re-open the issue if there is more to discuss.