A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

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

stuartpb
2015-04-02

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).

stuartpb
2015-04-02

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>
stuartpb
2015-07-10

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).

jonathank
2015-07-10

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

Anonymous2292900
2022-12-29

Hey! great proposal.

suns
2023-01-02

<keygen> is just a form signature, as I understand a substitution for secure connection in the times where https was not required. Definitely not a same purpose.

may be better as a separate input that is derived from another input

Separate input would not exclude the submission of original salt field in the form which would defeat the whole purpose of the proposal. Instead, another proposal could be made to

  • fill out the form and submit action points to another form filled with transfromed data. The chain of form submissions would give ability to mix various tokens and server-side generated data like CSRF token.

Alternate formulation example

Proposed method of silencing the password by omitting the name attribute and using id is not scalable as document could have multiple forms with fields of same name. Unique ID would be problematic.

Perhaps you want to introduce the omitsubmission kind of attribute to omit unwanted fields in the form and keep salt reference by name attribute. Or imply if the input field used by salt would not be submitted?


At the moment the `Declarative Web Application` concept and series of proposals are in baking. It would cover the given scenarios without JS enabled in a different more generic markup syntax. LMN if want to participate.