HTML Server Relief: Password Input Attributes for Client-side Hashing


#1

This is something I’ve wanted for around a decade: a polyfillable, pure-HTML standard for making it so that clients compute a hash of the password before sending it to the server. This allows servers to offload resource-heavy key derivation algorithms (like PBKDF2, or scrypt) off to clients (who can then compare the strong derived key using a much more light-weight hash function).

Example

Ideally, it would work something like this:

<input type="username">
<input type="password" crypt="bcrypt"  work="cost:11" salt="username">
<noscript>Your do not have JavaScript enabled; your login may be significantly delayed.</noscript>

The “crypt” attribute names the algorithm that should be used by the client to hash the password on submission. The “work” attribute defines the work parameters for that function to be configured with. The “salt” attribute defines the ID of an element to derive the salt from. (This could maybe be expanded to allow for multiple fields to derive salts from.)

(Edit: renamed “hash” to “crypt” to avoid resemblance to the unrelated “hash” property on HTMLAnchorElement.)

On salts

If a server wishes to use per-user salts, it should first prompt for a username, then send (or generate via AJAX) a second form with a hidden input containing the user’s salt. (Requests for non-existent users may either be responded to with an error, or with a fake salt, depending on whether or not the server wishes to disclose user existence.)

Alternatively, clients may elect to use a fixed global salt (a “pepper”), or to eschew salts together, as salts are not as necessary as they once were.

Polyfilling

A polyfilling library may check for WebCrypto implementation of an algorithm, and fall back to an (HTTPS loaded) script implementing any algorithm not supported by the client.

Due to the way the DOM event bubbling model works, without a synchronous WebCrypto API, the submit event will need to be cancelled, then re-emitted after the hashing completes.

Legacy Support

In the event that a client which does not support Server Relief has scripting disabled (so the relief cannot be polyfilled), their plaintext login attempt, detectable by not matching the syntax of a valid password hash, may be placed into a limited server-side stable of hashing (where a fixed number of requests may be hashed at a time).


#2

Actually, this may be better as a separate input that is derived from another input (specified by ID rather than name):

  • It clearly distinguishes between a hashed password and a non-hashed password
  • It allows forms to not submit the password (saving space and potential leakage)
  • It allows for chaining, for servers that wish to use a nonce
    • Doing this correctly requires saving a public key derived from the password, any existing standards for which I don’t really know of. It would also probably require a third attribute.

Perhaps better than “salt” would be an attribute defining the complete input string as an ES6-style template, like “#{salt}#{password}”. There could then be a second attribute for “message”, for algorithms that use a public key. (Alternately, the “value” attribute could be re-used as the content for the nonce, with an unchanged value being the sentinel of a failure.)

Also, it seems like nonces could potentially be used in place of CSRF tokens for login.

Alternate formulation example:

<form>
<input type="hidden" id="nonce" value="2015-04-01T12:25">
<input type="hidden" id="pepper" value="abcdef123456">
<input type="text" name="username"><label for="username">Username</label>
<input type="password" name="plaintextpw" id="password">
<input type="hidden" name="validation" crypt="pbkdf2-ecdh" work="iterations:86000" basis="#{pepper}#{username}#{password}" message="#{nonce}">
<button type="submit">Log In</button>
</form>

#3

I just found out about the <keygen> element: it seems like this is 80% of the way toward what I’m looking for above (in terms of being an existing element that already has a defined form behavior regarding crypto inputs). Sticking a pin in that to revisit next time I come around to this spec (of course, right now I’m more in the “passwordless by default” headspace that makes this kind of auth mechanism largely unnecessary).


#4

Also relevant is the Credential Management API and this issue: https://github.com/w3c/webappsec/issues/250


Can we get a "Security" category?