
Ask YC: Best Practices for User Authentication - markbao
Hey,
I'm wondering about the practices about authentication with regard to passwords.<p>I'm looking to exclude the HTTPS / SSL cert method, which is obviously the most secure method, but it's not completely within reach right now.<p>What I'm currently looking at is simply a Javascript SHA2 implementation that hashes the password before it is sent to the server. After reading a recent post (http://news.ycombinator.com/item?id=205420), it seems that this implementation isn't the best.<p>Here's what I got from that:<p><pre><code>     +----------+           +----------+
  +-&#62;|  SERVER  |----------&#62;|  CLIENT  |
  |  +----------+   nonce   +----------+
  |       |                      |
  |  SESSION["nonce"] = "1234"   | SHA(nonce + password)
  |                              |
  +------------------------------+
</code></pre>
(sorry for ASCII drawing failure)<p>What is good practice for user password authentication without SSL? (Feel free to yell at me for not using SSL, but I'm currently not able to implement it.)<p>Thank you.
======
jsjenkins168
As was pointed out by tptacek, this approach is not secure because the
javascript can be rewritten in transit. Also consider that javascript-based
hashing will be extremely slow and could interfere with the user experience if
the browser bogs down. And IE may show an error that performance is slow on
your site.

There just is no way around it, the only way to be secure is use SSL to share
secrets when you register. From then on you can just transmit an authenticator
(stored in a secure cookie) comprised of an expiration timestamp, any
identifying user data (like userID), and a non-malleable MAC digest of the
expiration and userID. Doesn't protect against replay but it helps to enforce
a short expiration for the authenticator. With this approach you'll only need
to use SSL for the initial login.

This is the best explanation of web authentication I've encountered on the net
[PDF]:

<http://prisms.cs.umass.edu/~kevinfu/papers/webauth_tr.pdf>

~~~
DaniFong
Depending on who you are, you could guard against javascript being rewritten
by a man in the middle attack by running, say, a greasemonkey script that
figures out when a password should be sent and hashing with the server domain
automagically, so long as you can cache it locally. In this way, admins could
secure clients against having their passwords stolen without any effort on the
part of web-app writers, or users. Even a browser, say, Firefox, could do some
version of this.

Really, digest authentication solves the same problem, but hardly anyone uses
it.

~~~
tptacek
I know you're just trying to add a layer here, but if you think this through,
you'll probably see that GreaseMonkey does not win against content-controlled
Javascript for its own site. Firefox has had a hard enough time just keeping
content-controlled Javascript from breaking into Chrome.

As a simple example, note the fact that the "when the password should be sent"
signal originates from content-controlled JS, which controls the DOM. Note
also that with same-origin out of the picture, the content-controlled JS can
choose to send the password whenever it wants.

~~~
DaniFong
Sure, it's not meant to be foolproof. I only wanted sometime to secure the
login for the large number of sites taking passwords with plaintext. Right now
it's completely a statistics game -- you can just stand outside someone's
wireless network capturing packets. If it's insecure or WEP, you'll be able to
recover the key, then grepping through with password=_ will eventually give
you a password to use on their favorite sites.

I do think security is a matter of degree. And currently authentication is but
a few removed from wide open.

~~~
tptacek
This is why app developers should be using SSL. I have no smart-assed
responses to an HTTPS login screen. No ad hoc hashing schemes required.

~~~
DaniFong
By the same logic, crackers shouldn't attack anything. But they do.

~~~
tptacek
I'm not sure I see how that follows.

But to reify this a bit, do a little Google research on what the banking
industry is dealing with regarding multifactor authentication. Nothing they
are trying is working, and they're doing considerably more than Javascript
hashing. The schemes being discussed here _are_ being attacked, successfully.

~~~
DaniFong
Not every app developer who could use SSL does, which can give away passwords
shared with SSL sites.

By the same token, some hackers capable of cracking do so, though they, in
some sense of the word, really shouldn't.

We're thinking about security from two different standpoints. If I lock my
door, but my glass window has no bars, I'll still say it's more secure than a
house with a door open. The issue, for me, is less that someone _can_ , but
whether someone _will_. If I make it harder for someone to mess with me, maybe
they won't.

~~~
tptacek
I can't win that argument; it's semantic. Just remember that the _majority_ of
the decision you're talking about belongs not you but your users. Don't offer
a false sense of security.

------
dfranke
What's the difficulty with implementing SSL? If you're not already familiar
with it, read the manpage for s_server, which is part of OpenSSL.

~~~
markbao
No difficulty in implementing SSL, but I'm a student and don't have much
money. Granted, this is a temporary solution until I'm able to get some money
together for a dedicated IP and SSL cert.

~~~
dfranke
You can do SSL very cheaply these days. GoDaddy has certificates for $15/year.
Dreamhost gives you a dedicated IP for an extra $4/month.

~~~
SA
Given the fact that how cheap it is these days to get SSL certificates and
static IPs I would take the route of SSL rather than going thru this untested
(read: not well tested) route of JavaScript encryption.

<http://www.hitlinkz.com>

~~~
swombat
Please don't spam your URL, especially not as your signature. That's (very)
bad form.

------
tptacek
No security scheme delivered over Javascript is going to make a difference in
a security audit. Your auditor is going to tell your prospective customer that
anybody who controls the DNS, routing, the network, ARP, the browser --- or 10
other places --- can rewrite the Javascript in transit.

If you can't do SSL, just do plaintext passwords. It means you're being open
with your users about the risk.

~~~
brfox
I'm no expert... but what if just the javascript was served via https? Maybe
somebody ought to put an MD5 javascript library on an https server as a public
service. Or would js hashing still be vulerable since the man-in-the-middle
could change the nonce or something?

~~~
ben_h
If you're going to serve Javascript over SSL, you need a full SSL setup
including a valid certificate, etc. If you have that, you may as well just
serve the login form itself over HTTPS, redirecting back to HTTP after logging
the user in, and forget the Javascript altogether.

~~~
ComputerGuru
He means someone set up a a JS library over HTTPS on a server for _others_ to
use. Like you would grab a JS library off of Google's HTTPS servers and use
that on your non-SSL pages.

~~~
tptacek
Uh... and the page that includes the JS, which is part of your app... comes
from where?

~~~
brfox
Oh yeah, good point!

------
jrockway
You've basically reimplemented HTTP Digest auth, although not quite as well.

The problem with your implementation is that the server needs to know the
cleartext password. With digest auth, the server never has the clear password,
(only a digest thereof) although it can use the digest to authenticate to
other services with the same digest.

It doesn't really matter though, since passwords are very insecure and users
know that. Nobody is going to give your web app an important password. If they
do, and it gets compromised, it's their problem, not yours. (For secure
authentication, look at how ssh uses public/private keys. Much better.
Compromising the server's password database will never yield the private keys
and hence is a complete waste of time for an attacker.)

Anyway, I tend to transmit passwords over SSL and then store them in the
database hashed with bcrypt ([http://search.cpan.org/~zefram/Crypt-
Eksblowfish-0.005/lib/C...](http://search.cpan.org/~zefram/Crypt-
Eksblowfish-0.005/lib/Crypt/Eksblowfish/Bcrypt.pm))

~~~
gommm
The problem is that most users are not programmers, are not at all interested
in computer security... So, no maybe most people here would not use an
important password for yc news... But the average user doesn't think about
this and knowing this can you really say that it's there fault for not being
educated on computer security?

Anyway, since you do the right thing: SSL + bcrypt, good but don't say it's
the user problem if the system gets compromised...

------
curiousgeorge
huh? whatever you do on the client machine you're still open to a man in the
middle attack . hashing passwords simply make it more difficult for
eavesdroppers to figure out the original password, and keep people from
passing potentially sensitive information across the net in cleartext.

use SSL for things you need to keep secure, like credit card payments. If you
can't set that up, outsource stuff to a payment provider like Paypal until you
get your house in order. Otherwise be more clear about your security concerns
in your write-up, so people know WHAT you are trying to solve instead of HOW
you are trying to solve... something.

------
johnm
In practical terms, just use HTTPS with rooted certs. It's not expensive for
basic usage. And, if you're just doing a for-fun project you can always use
self-signed certs.

If you want to go deeper down the rabbit hole check out SRP:
<http://srp.stanford.edu/>

In terms of dealing more securely with data on your server, check out the
book, Translucent Databases ( [http://www.amazon.com/Translucent-Databases-
Peter-Wayner/dp/...](http://www.amazon.com/Translucent-Databases-Peter-
Wayner/dp/0967584418) )

~~~
tptacek
Stay away from SRP. Browsers don't support it natively, and there are (so far
as I can tell) no peer-reviewed libraries for it for the major web stacks. SRP
is easy to get wrong.

~~~
johnm
I did say to use SSL in practice, right?

Reading about SRP would help solve much of confusion people seem to be having
in many of the discussions going on in this thread.

------
tlrobinson
Hashing the password on the client with a nonce provided by the server and
checking on the server is good for login, but not registration. The server
still needs the password (or a hash of the password and a salt) in order to
compute the login hash for comparison.

The only truly secure way (that I can think of) is some sort of public-key
encryption, either SSL or some JavaScript library.

~~~
markbao
That's very true. Damnit. Not sure how I'm going to fix that.

I've looked into public-key encryption, and unless if I generate a new public
and private key for every request to login, replay attacks are still possible.

~~~
DaniFong
If you hash the password on the clientside, say, and then use that as the
password in your scheme throughout, then you don't need to transmit the
password in cleartext, ever.

~~~
tptacek
This is a win because, even though you're sending something _password-
equivalent_ over the wire, at least you're not exposing a password that's used
on other applications?

~~~
thaumaturgy
Can we have it both ways? Transmit a password-equivalent over the wire,
without compromising the security of the password database?

How about using a cryptographically strong hash on the client side,
transmitting that hash to the server, and then having the server perform
another cryptographically strong hash on the hash it receives, and compare
that against its database? This way, the server isn't storing the passwords in
the clear anywhere, and there is still at least some protection against having
users' passwords picked out of the air -- even, I think, during the user's
initial registration.

I realize that any client-side code designed to do this can be compromised by
a man-in-the-middle attack, forcing the user's password out into the open.
This isn't meant to be perfect security, it's just meant to provide one extra
layer of protection for the individual user's password(s), without
compromising the database as a whole.

~~~
DaniFong
Yes, that is what I suggested.

~~~
thaumaturgy
Ah, I didn't initially read it that way.

It still leaves the problem of password sniffing over an unsecured network,
but I think that could be solved by generating a salt on the server for each
login request, and then transmitting that salt to the client, where it's used
to re-hash the password-equivalent. That would also help protect against
something like a DNS compromise, since effectively every user's password
changes every time they log in.

There are still plenty of ways to break this scheme, but I think at this point
it's Sufficiently Hard (tm) enough to be suitable for an online forum.

...and I imagine that somewhere, tptacek is screaming, "Just use SSL!"

~~~
DaniFong
I mean, there are a few standpoints here: if you're running a site, use SSL,
if you're a user, don't reuse passwords or use something like PWDHash, or if
you're working on a browser or you're an admin, consider a way, like PWDHash,
of making it so that a password compromised at one site doesn't compromise any
others.

------
tptacek
Also: any security scheme based off a simple hash function is going to require
you to store cleartext passwords on the server side, meaning that any SQL
injection vulnerability compromises, say, 10,000 user passwords.

~~~
markbao
Actually, I made a mistake in the diagram. The password will be hashed first,
then the sha2(nonce + sha2(password)). Passwords will be stored server-side as
sha2(password).

Can't update the post now, though.

~~~
theg
Your method for storing the passwords server-side is actually not very secure.
Hashing the passwords without a unique salt is not a secure way to store them.

For one thing, every user with the password of 'secret' will have the same
hash in your database. Secondly, if I were to steal all of your password
hashes, I would just have to compute sha2 of every 6-8 (or whatever password
length you are using) character string. With a 32 character salt, I would have
to compute a sha2 of every 38-40 character string, making my job a more time
consuming.

~~~
markbao
The thing with a salt is that - would you include the salt in the Javascript
hashing system? like sha2(nonce + sha2(password + hash)) ? If so, then the
salt isn't really that useful - you can then just compute sha2 of 6-8 letters
+ salt.

~~~
gommm
The only usefulness of the salt is if somebody steals your database and uses a
rainbow table to try to get the passwords. If you don't have salts for each
users, then a pre-generated rainbow table for sha1 based on common passwords
is going to be very efficient at getting the passwords for each account. If
you use salt however, you are protected from the use of pre-generated tables
since the attacker has to brute force all the combinations for each user. If
you use sha1 or md5 it's still not too secure since sha1 and md5 are fast to
calculate and getting faster with this : <http://nsa.unaligned.org/hw.php>

Read [http://www.matasano.com/log/958/enough-with-the-rainbow-
tabl...](http://www.matasano.com/log/958/enough-with-the-rainbow-tables-what-
you-need-to-know-about-secure-password-schemes/) from tptacek, it's very
informative...

Of course, the big problem with the javascript way explained here is that you
send sha2(nounce + sha2(password+salt)) This means, that anybody who get
access to the database can send sha2(nounce + hash_from_database) So basically
in this case using salt in the database prevent people from getting the
password too easily (so it sorts protects the attacker from getting a password
associated with an email address that he could try later on paypal), but it
doesn't protect the account in any way...

And for those who think that it's unlikely for anybody to get access to the
database, it happened to the reddit guys...

