

Ask HN: Which login method do you use? - apstuff

I'm at the point where I need to implement login capability on my main page.<p>What method do you use? php with a database? cgi against a protected file? Clickpass.com like HN.<p>Or did you roll your own?
======
jmtame
I have always created my own. Although I've been doing PHP, I'm currently
using RoR and it has a plugin that handles all of this. PHP with a database is
very easy, especially if you use CodeIgniter, there are form validation helper
classes.

On registration:

\- Ask for username and password (do form validation, ie passwords match, xss
clean, etc). toLowercase() the login.

\- Create a hash of some type for the password. This becomes used in the
database, and again on login. If you're not worried about security, md5 your
password, store it in the db. Otherwise, look up a salt hash.

\- I typically log the user out and then require them to log in and create a
session after they registered.

On login

\- Ask for username and password, toLowercase() the login when checking

\- Run the same md5 or salt hash against the password, check if the # of rows
in the database is > 0, if it is, log the person in and give them a session
with a value of "is_logged_in" to true or something similar. Also pull the
database user_id or e-mail and use that to remember which user you're dealing
with.

\- If the # of rows found in database is == 0 (where the login and pass equal
those from your post variables), the login failed

~~~
tptacek
Please don't just use MD5 or SHA1 with a salt.

[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/)

~~~
thorax
I upvoted you because that's a good link and good advice in a sense. I don't
agree fully with your exact phrasing and would put it a bit milder for someone
asking this kind of question.

I'd personally advise: Use a respected library or _at least_ an MD5 or SHA1
approach with a strong salt. There are better ways that you should consider
(link), especially if you're writing an authentication package for reuse by
others.

~~~
tptacek
It's painful to see someone recommend "at least" MD5+salt, when that solution
is a single for() loop away from being at least adequate. If you're wondering
why I'm using such strong words, it's because you talked about "strong salt"
(which means nothing), but ignored stretching, which actually does improve
security.

SHA1+"strong salt" is extremely weak. It mitigates only one attack, which
every respected authentication system has been invulnerable to since the '70s.
I blame Microsoft for reviving rainbow table lore, but still, thorax. Come on.

~~~
thorax
Your article is great advice. As I worded it, I'm providing practical advice
to a new web developer. Let me elaborate on my position:

First and foremost, don't write the password handling yourself if you can
avoid it. If you do it yourself, I'm only firing the developers who put the
password in plain text, and I'm not even going to give grief to the ones who
at least use a rainbow-dodging salt and hash (like what most major web
frameworks include by default for user auth management). I.e., if they use
django defaults or Code Igniter defaults, then they're not in trouble.

Any senior engineers on the team are going to get some whining from me if the
framework supports crypt/bcrypt and they didn't enable that, but if they
forgot and the site launched without it, I'm not going to die.

It the team is making an authentication package for a web framework or
_especially_ for a native framework, they need to consider heavily using
bcrypt (or other state-of-the-art approaches) for password handling unless
there's some major compelling reason we cannot or should not.

What I'm trying to be is realistic and give the guy a side that's non-
religious.

I agree with you as a hacker, but on the practical side, no coding decision is
all-or-nothing with me.

If you're a new developer and have read this far and want to know where to
find decent bcrypt packages for your favorite language, Google's AWT page has
a good explanation and handy links to those (scroll down):

[http://code.google.com/p/google-web-toolkit-
incubator/wiki/L...](http://code.google.com/p/google-web-toolkit-
incubator/wiki/LoginSecurityFAQ)

Also here's instructions for using _crypt_ with Django auth:

[http://docs.djangoproject.com/en/dev/topics/auth/#changing-p...](http://docs.djangoproject.com/en/dev/topics/auth/#changing-
passwords)

~~~
tptacek
Two responses.

First, we don't have pages and pages of comments and discussions because the
topic isn't cut-and-dry. The topic is cut-and-dry. It just takes 5-10 round
trips to explain to someone why clientside Javascript crypto is a bad idea.

Second, I agree with you. I'm not firing someone for using SHA1+nonce. But I
will bitch if you recommend it, because even though it's not a game-over
mistake, it's still a mistake.

------
dryicerx
Since most people already talk about the backend of it, let me share how to
securely send the password from the browser to the server encrypted, instead
of simply in clear text. (when you can't use SSL for some reason)

\+ Server has your passwords stored as sha1(password+salt(password)). salt
function isn't secret (eg. reverse the text)

\- Client visits login page

\- Website generates random token. Then sends back HTML with the random token

\- Client generates passresponse = sha1(token + sha1(password +
salt(password)))

\- Client sends the passresponse, token, and username back

\- Website checks for existence of token, removes it, then computes it's own
sha1(token + password_hash_from_db) and checks against the sent passresponse.

This way the password is never sent in clear text. Unlike HTTP authentication,
this works nicely with html forms since you can do all the crypt in js. Then
again, this might be a bit overkill... and using SSL is probably a better
option.

Just sharing another solution.

~~~
tptacek
You're describing the simplest possible challenge-response scheme. It has two
problems, both severe enough that you shouldn't recommend people waste time
implementing it:

* First, because no browser bakes this crypto protocol in, you have to deliver it over Javascript. The protocol basically stipulates that you don't have SSL/TLS. So all you've done is move the goalposts. No matter what kind of dance you do (for instance: Meebo actually delivers a JS implementation of RSA!), the action is now in the JS delivery, which is trivially compromised.

* Second, secure authentication schemes aren't vulnerable to trivial dictionary attacks. This one is: the attacker is stipulated to have access to your traffic. She sees the nonce the server sends and the hash the client responds with. She can solve for the password by (very fast) brute force against a wordlist.

~~~
dryicerx
To your first point, yes js would be doing the crypt, if the js delivery can
be compromised, then the login html delivery can be equally compromised (which
would send the login information somewhere else)

I agree with your second point, a eavesdropped can use a dictionary attack. It
makes it just a tiny bit harder for them since they need to generate their own
cleartext-crypttext and cannot use a pregenerated table.

I am curious, is there a better way to do this (other than SSL or using RSA)

~~~
tptacek
In that first sentence, you need to take the we word "if" out. The exact same
attack that motivated you to come up with the challenge-response scheme works
against the JS delivery.

In the third sentence, take the "or RSA" out. There's no way to get a browser
to safely do RSA authentication without SSL.

I have good news for you. The answer to this problem doesn't involve complex
technology. What security practitioners are going to recommend to you is, just
put up a login page, and send usernames and passwords. I have just released
you from having to waste time and energy thinking about this.

------
tptacek
Most every web application my team assesses just uses a database of hashes.
This is fine; just try to make the hash function take a long time to run
(speed is the enemy here). I highly recommend "bcrypt", a routine available in
almost every dev environment --- and typically in the better plugins --- for
generating safe auth hashes.

------
mk
django.contrib.auth

------
modoc
I have my own code I use on my projects. It uses secure SHA 256 hashing for
the passwords. The code handles registration, login, logout, and forgot
password flows.

~~~
e1ven
We pass the login and passwords to an openldap server running internally, and
then get back the success/fail message.

This is the best strategy for us because it allows us to offer a wide array of
services running through our accounts, using out of the box software..

We can tie the forums into LDAP without writing our own, as well as our
internal Jabber server, etc.

Once login has completed, we give the user a 128-bit sessionID, which we use
for all further communication, until their session expires.

------
kineticac
Just put the TwitterAuth gem into my rails app, and am using OAuth with
twitter now. This is a niche though, meaning unless you already have twitter,
or actually like it, it's a long process and could keep people from signing
up. Logging in is easy though.

<http://kineticac.posterous.com/rails-and-twitter-signin>

------
javanix
I am working rolling my own with Struts/JSP.

It seems pretty straightforward (hash pass, place on server, and check
against), but I need an easy way to compute an SHA hash in-browser, so the
server doesn't have to receive the pass in plaintext.

Anyone know of a way to do it with Struts/JSP, or even JS if its not too slow?

~~~
tptacek
This is kind of silly:

* The hash you send will probably be password-equivalent; losing it to an attacker is just as bad as losing the password.

* If you're delivering the JS to generate the hash over HTTP, you have exactly the same threat model as with plaintext passwords (attackers will just subst a script that sends the raw password).

* If you have working HTTPS, you already have optimal communications security; just send the password.

* Even if you came up with a challenge-response protocol to make the hash non-replayable, the exchange itself would be vulnerable to a trivial dictionary attack.

Don't bother with this idea. Move on to something that will add real value to
your app.

------
lsc
I use http auth

apache has modules to hook it up to just about any backend; it's supported by
all browsers, and it's easy to automate against.

I would be interested in knowing why more people don't use it.

~~~
tptacek
Because, at least with mainstream browsers, users can't log out.

You can fail certain formal security audits for using HTTP authentication.

~~~
lsc
Speaking of the audit problem, do you have a link? is this just for http
basic? or is this the case for http digest as well? what about negotiateAuth
with mod_auth_kerberos or the like? is this only because of the logout
problem? or because of the problems with basic auth?

hm. I know you can logout by going to
[https://username:boguspassword@thesiteyouarelogingoutfrom.co...](https://username:boguspassword@thesiteyouarelogingoutfrom.com)
but that will ask you to re-input your password, usually, making it unsuitable
for a 'logout' button usually, I think. I wonder if there is a js workaround
for that.

------
kineticac
We're using Rails as the framework and restful_authentication plugin for
logins. Moving forward we are also going to integrate OAuth for things like
Twitter logins, Facebook Connect for facebook, etc.

------
SemperUbi
HTTP Basic + https

~~~
lsc
how come you don't use http digest?
<http://en.wikipedia.org/wiki/Digest_access_authentication> it is
significantly more secure without https

~~~
tptacek
Because digest requires you to store the plaintext of the password on the
server, making any database or filesystem exposure a calamity for all your
users.

~~~
lsc
do you think a hashed password is going to last long against an attacker?
considering how cheap computing resources are (and the common use of botnets,
and the fact that most passwords are dictionary words) I treat password hashes
as if they were cleartext passwords.

But I suppose that if you are using https, you get most of the advantages of
digest anyhow.

~~~
tptacek
If the password is hashed well, with stretched SHA1/SHA256 or (better yet)
bcrypt, then yes: breaking the hash would involve a significant advance in
cryptography.

~~~
lsc
hm? I'm trying to say that once you have the hash, you can run a dictionary
attack against it without any advances in anything. I can use whatever
procedure the server uses to verify logins, and just try passwords. You can
make the dictionary attack more expensive by using an expensive hash like
bcrypt, but that's going to slow down your app, too. (http auth re-
authenticates every page load.) so really, you can't make your hash
calculation any slower than, say, 50ms without users complaining.

Lets say you can crack the average user account with 40,000 hits from a
dictionary attack (I imagine most passwords fall much faster) if each lookup
takes 50ms, 20 lookups a second, it'll take around 30 minutes of cpu time to
crack each password. assuming a reasonable-sized botnet, that's not much.

~~~
tptacek
You're not thinking this through. In no well-designed web application is
password checking in the 80/20 hotspot of performance. In fact, if it's within
a light year of mattering to performance, you've done something horribly
wrong.

The point of adaptive hashing is that doubling the cost of the hash on the
serverside adds negligable overall cost, but doubling the cost of the hash on
the attacker's side _doubles their cost_. This is not a complicated tradeoff.

~~~
lsc
hmm? http basic auth checks your password on every request. you can do auth
other ways, (but you need to be very careful.) but that's how http basic auth
works.

the attacker can check one word from her dictionary in the same amount of time
it takes you to authenticate one user for one page.

if you make that check take longer than 50ms, it will start slowing down your
webapp.

at 50ms, an attacker can check 40,000 words in around half an hour.

you can double that to 100ms, but at that point you are starting to slow down
page loads, and it's still only taking the attacker an hour to run through
that dictionary.

~~~
tptacek
Don't use basic auth, lsc.

 _[Edit] I regret even conceding this point. Even if you use basic auth, the
tradeoff here is not complicated._

~~~
lsc
the bigger point is that there is a limit to how slow you can make the
password checking process. Ok, so let us assume you securely authenticate once
per session. How long can that authentication take? I suppose you can put up a
little clock... make it take a second and we are talking 11 hours per
password, which is starting to get significant... but my point is that the
small search space provided by user chosen passwords means that if you make
the hash function slow enough to stop an attacker, you are moving into time
spaces that users will notice.

------
matticakes
<http://www.openwall.com/phpass/>

------
dmanxiii
OpenID, if applicable.

~~~
frossie
Comment from the user end: I am not the greatest fan of OpenID (try talking
your grandma through getting one) but I agree that as long as you don't have a
site where the user might have a real need for unique authentication
information (eg. bank account), you should really consider something like
clickpass.

I always try new sites if I can get in with existing authentication - and I
don't always if I need to register. So it depends how much you care about
uptake.

------
rincewind
ssl client certificates

~~~
tptacek
I can't understand why making SSL client certs a totally seamless experience
for end-users isn't the #1 security priotity for the IE and Firefox teams.

------
rguzman
facebook connect

~~~
coopr
Anyone have any pros or cons of Facebook Connect? Besides the obvious con of
requiring your users to be Facebook users too?

------
zmonteca
We used a combination of Cake Auth

