Webauthn: explain challenge's security importance and use in both registration and authentication operations

Created on 14 Apr 2017  路  16Comments  路  Source: w3c/webauthn

need to expand the explanation of "challenge" in the spec -- challenge is used in both regstn and authn protocol runs and has security implications. It MUST NOT be created on the client-side -- rather, it MUST be randomly created on the server-side with very low probability of collisions, and verified upon receipt back from the client and end of protocol run in order to provide protocol-run round-trip integrity. this needs to be explained in the spec and the code in the examples altered to reflect this -- especially since developers will simply cut'n'paste our examples! :-P

security-tracker impl-cons editorial

Most helpful comment

"and the client's responses' challenge fields" - bit of a mouthful. "and the challenge in the client's response MUST..." ?

All 16 comments

https://github.com/w3c/webauthn/issues/88#issuecomment-288234647 proposes a way to write these MUSTs, which constrain the behavior of the server rather than the browser or authenticator.

wrt the examples (aka "sample scenarios"):

var challenge = new TextEncoder().encode("climb a mountain");

perhaps ought to be:

// server generates 32 byte random challenge value, memorizes it for validation of the upcoming 
// response, base64-encodes it, and sends it to client as part of the web page running in the user 
// agent, who then turns it back into a byte array thusly:
var challenge = window.atob("PGifxAoBwCkWkm4b1CiIl5otCphiIh6MijdjbWFjomA=");

this is applicable to all the examples in this section.

atob() returns a DOMString rather than an ArrayBuffer.

Uint8Array.from(window.atob("PGifxAoBwCkWkm4b1CiIl5otCphiIh6MijdjbWFjomA="), c=>c.charCodeAt(0))

would work.

cool, thx @jyasskin -- hm, I am gonna have to dig around and figger out the details on how this "mapFn" gizmothingee works: c=>c.charCodeAt(0)

Did y'all consider requiring the value to be a base64-encoded DOMString, and doing the decoding/bufferizing as part of the makeCredential and getAssertion algorithms? base64 seems like a sufficient hurdle that folks wouldn't generate it themselves client-side, and they wouldn't have to come back to the spec to look up how to convert a string into an ArrayBuffer. I know I would have had to look that up...

@mikewest:

y'all consider requiring the value to be a base64-encoded DOMString, and doing the decoding/bufferizing as part of the makeCredential and getAssertion algorithms?

no, we did not think of that -- yes, I'm thinking your suggestion is the way to go -- thanks!

@equalsJeffH @mikewest Are you proposing we change the challenge to be a DOMString? @vijaybh @leshi I remembered the reason why challenge is base64url encoding is partially because of CTAP, correct?

@equalsJeffH @mikewest Are you proposing we change the challenge to be a DOMString?

I'm suggesting that, yes. Coupled with changes to the algorithms which would throw a TypeError if the string isn't a valid base64url-encoded string, that seems sufficiently unlikely to be a) generated client-side, or b) confusing. :)

@vijaybh @leshi I remembered the reason why challenge is base64url encoding is partially because of CTAP, correct?

For clarity, the challenge property is currently a BufferSource. We base64url-encode it as part of the algorithms above when creating the CollectedClientData object. I'd suggest we simply drop that, and expect the developer to provide a pre-encoded string.

The challenge was originally a base64 encoded DOMString. We changed it to BufferSource in response to TAG feedback. See #61.

@slightlyoff: How do you feel about the BufferSource recommendation, given the code snippets discussed in https://github.com/w3c/webauthn/issues/404#issuecomment-294240502?

The challenge was originally a base64 encoded DOMString. We changed it to BufferSource in response to TAG feedback. See #61.

thanks @vijaybh -- this seems to be a lesson that we now have long enough history that we ought to search prior issues/PRs rather than rely on memory (as I mistakenly did)...

Assigning this to @equalsJeffH because he proposed the issue.

I'm fine with working on this issue. we still would like some input from @slightlyoff it seems.

Re: https://github.com/w3c/webauthn/issues/404#issuecomment-294240502 and https://github.com/w3c/webauthn/issues/404#issuecomment-294236973, let's not hold their hands about how to get the octets from their back-end to WebAuthn. I suggest this compromise:

// server generates 32 byte random challenge value, memorizes it for validation of the upcoming 
// response, and sends it to client as part of the web page running in the user agent, who then
// turns it back into a byte array thusly:
var challenge = new Uint8Array([21,31,105 /* 29 more random bytes generated by the server */]);

And in the Security Considerations section, add:

== Challenges == 
As a cryptographic protocol, Web Authentication is dependent upon randomized challenges
to avoid replay attacks. Therefore, the [=challenge=] fields MUST be randomly generated
by the [=Relying Party=] in an environment they trust, and the client's responses' challenge
fields MUST match what was generated. This should be done in a fashion that does not rely
upon a client's behavior; e.g.: the Relying Party should store the challenge temporarily
until the operation is complete. Tolerating a mismatch will compromise the security
of the protocol.

LGTM - thanks!!

"and the client's responses' challenge fields" - bit of a mouthful. "and the challenge in the client's response MUST..." ?

Was this page helpful?
0 / 5 - 0 ratings