[whatwg] Hashing Passwords Client-side

On Thu, Jun 16, 2011 at 3:59 PM, Sean Connelly <sean at pbwhere.com> wrote:
> ## Proposed Solution:
>
> Add an attribute to <input type="password"> called "hash". ?For example:
> <input type="password" hash="sha1" salt="something">

How does this solve the problem?  Do you expect it to help because
users will be able to see which sites are using unhashed passwords,
and complain so that the site admins fix it?

> This will indicate to the browser that it needs to hash the value locally
> before sending it to the server. ?This hash should include a site-specific
> salt, so that the same password typed on two different sites will hash to
> different values. ?I propose the default salt to be the origin as an ASCII
> string (protocol + host + port, ex: "http://example.com:80"), and the
> default hash to be "none" (in order for backward compatibility).

Salting with something that's not included in the database is not a
good idea.  It means that if the site author isn't extremely careful
to provide the same salt every time, login will fail.  If the author
can provide a salt, it should default to the empty string, and it
should also be included in the submitted hash.  E.g., instead of
submitting "0123456789abcdef", submit "0123456789abcdef:salt" or
something as the password.  That way, if anything is stored with the
wrong salt, it will be easily detectable on the server side.

Also, the site should really be using a per-user salt.  Otherwise,
someone with database access can immediately tell which users have the
same passwords.  For large databases, this would allow recovering a
lot of the passwords very cheaply by frequency analysis.  However,
it's probably not practical to do a client-side per-user salt.

One thing salting would be good for is some kind of nonce scheme,
where you compute the hash, salt it, and then hash it again with a
one-time salt.  If the server discards the salt after using it once,
the password hash sniffed off the wire will be useless to the
attacker.  (Of course, this only works for login, not registration.)
However, you need fairly complicated server-side logic to support
this, and it's not trivial for users to notice whether the nonce is
reusable, so I don't know if it's useful to support this.

So I think salting can just be omitted here, at least for a first
draft.  Site-wide salts don't buy you very much, and clients can't
easily do any other kind of salt.  The salt attribute as you proposed
it won't significantly increase security, but it will make people
think they're doing proper salting when they aren't.

Not supporting salts does leave us open to rainbow tables,
unfortunately, but I don't see a good way to fix that from the client
side.

> In order to deal with migration correctly, the browser will also need to
> communicate to the server that it correctly performed the hash.

Why?  The server can first try comparing the submitted password to the
stored hash, then if that fails, hash the submitted password and
compare that to the stored hash.  That way, you don't need to submit
out-of-band information.

> I propose a
> new header for the browser to send:
>
> X-Password-Hash: 1

If we did want a new header, it shouldn't start with "X-".  We'd be
standardizing it, after all.  We'd probably want to submit it as extra
magic form fields, too, not an HTTP header.


I'd suggest a way to allow authors to iterate the hashing.  For
password hashes, you shouldn't use a hash applied only once.  Instead,
you should hash the same value thousands of times.  That way, an
attacker who gets the database will have to spend thousands of times
the CPU effort to crack the hashes.  I suggest again that the number
of iterations be adjoined to the password in some fashion, so that
it's easy to migrate passwords if you increase the number of
iterations.  I'd also suggest a high number of iterations by default,
so that a low-end client would take 10-100 ms to execute the hashes.
There should be a minimum number of iterations too, to help avert
authoring errors.

> 1. Host never has access to actual password (as long as user has a modern
> browser)
> 2. If the host is compromised, hackers may be able to takeover the account
> on the server, but will not be able to take over accounts on different
> servers even if the user uses the same password (because the hackers will
> only have access to the hashed password with site-specific salts)

These are not benefits relative to doing it on the server side.

> 3. Plain-text passwords cannot be sniffed over HTTP

This is a definite benefit.  Barring one-time salts, the attacker
could still authenticate as the user by just submitting the same hash,
but it's still good if they can't retrieve the plaintext password.  If
they don't know the original password, they can't attack other sites
where the user has an account.

> 4. Easy for webmasters to upgrade for additional security benefit

It wouldn't be any easier than with server-side hashing.  In fact it
might be mildly harder.  But it would be *detectable*, which I think
is a possible advantage.  Security-conscious users could complain if
an application isn't hashing client-side.  I'm not sure how
significant this advantage is, but it might be significant.

> 1. Host cannot validate password requirements (ex: 2 upper case, 2 lower
> case, 2 special characters, password length, etc)

These could be validated client-side, possibly even using the pattern
attribute.  Of course, then it wouldn't be totally reliable; but if
the user is going to the effort of disabling that JavaScript, they
could just as well go to the effort of having it auto-mangle their
password to meet requirements.

Alternatively, authors could just omit the hash attribute when the
user is creating a new account or changing their password, and hash
server-side before storing the new password.

> 2. Server-side code might be complicated for dealing with legacy,
> non-hashing browsers

This is already the case if you do server-side hashing, if you've ever
changed your hashing method in the history of your application.

> 1. How to deal with the character encoding of the page correctly? ?Should
> everything be converted to UTF-8 before the hash is calculated?

Something like that sounds like a good idea, yes.  What happens now if
you submit a password in a different character encoding than you used
to set it, does it just fail to work?

> 2. What level of access should JavaScript have? ?Should it have access to
> read the plain password, or should it only be able to read the hashed value?

Restricting its access is pointless from a security perspective, since
it could just remove the hash attribute and the user wouldn't know the
difference.  Or set onkeypress handlers, etc.

On Thu, Jun 16, 2011 at 5:08 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
> Personally, I'd prefer the information be transmitted via another
> (browser-synthesized) form input, as it's usually much easier to read
> form inputs than header values.

It doesn't need to be transmitted at all.

> I like your idea for the default salt. ?We might be able to hook off
> of slightly better concepts (use the origin directly?) but the idea is
> sound.

Only until the site changes origins and all logins break for no
apparent reason.  Or if the site is accessible from multiple origins.
Having a default salt that's anything other than a fixed string is a
really bad idea.  Plus, it's not a replacement for per-user salts, and
authors will think it is.

> This is a benefit, actually. ?Password requirements are, nearly
> uniformly, absolutely horrendous for security in practice.

Imposing a fairly high minimum password length and banning dictionary
words or simple combinations thereof will make it nearly impossible to
brute-force the hashes, which is a huge security benefit.  It's
uniformly a plus against network attacks or server compromises.  It
might make it more likely that users will have their browser remember
their password, which will worsen client compromise.  But that's
insignificant if you impose only mild restrictions (e.g., at least
four characters), while the added protection against server or network
compromise in that case is very large in that case if you use a slow
hash.

I mean, do you really think it's bad for security for sites to ban
zero- and one-character passwords?

> Only for the transition period. ?Afterwards, you can just ignore
> legacy browsers and store the passwords directly. ?Those older
> browsers will just have security vulnerabilities.

No, they'll fail to work.  If the user creates an account with an old
browser and then tries to log in in a new browser, or vice versa, the
login will fail.  That will only be tolerable in the far future, when
the old browsers are really insignificant.

> The .value property and the value actually submitted should be
> identical. ?This indicates that, unless we add something extra, JS
> would only get the hashed value.

I think it's better for script to get the unhashed value.  Otherwise,
the client would have to synchronously compute the hash every time the
value property is accessed, which would be a bad idea if it takes 50ms
or something.  Also, with the unhashed value you can do things like
check length and so on.  Validation attributes like pattern should
also apply to the unhashed value.

> Overall, I like the idea. ?It seems like a pretty clueful addressing
> of the topic, and it directly addresses the problem that servers
> shouldn't ever remember passwords, but a lot of them do. ?Finally, it
> puts the processor cost of good crypto-hashing on the client rather
> than the server, which is nice. ?We can do a nice, expensive hash on
> the client without burdening the user, while an expensive hash *can*
> be a minor issue for busy servers.

I also like the fact that it can give users warning or assurance about
security problems.  It would be nice if we could have password fields
with a hash attribute display slightly differently, so clueful users
could be sure our passwords are being handled at least somewhat
securely.  It only takes a small minority of users to complain to
encourage the site author to fix it.

Received on Thursday, 16 June 2011 14:38:09 UTC