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
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.
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.
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):
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.
Rainbow Table is unavoidable. Block those IPs which have more than certain times of failed password. And, people usually cannot access to those hashed passwords.
(1) Not only are rainbow tables avoidable, but they've been trivially avoidable since Unix crypt(3) was invented in the '70s. The only way you can become susceptable to them is if you make the mistake of designing your own scheme. So don't do that.
(2) There's a reason that no mainstream consumer application actually does this: as soon as you lock a normal user out of their account for an hour, you probably lose the user forever. Ok, there are two reasons: this technique doesn't add any security. Anybody nuts enough to brute-force your login page has as many IPs as they want. But that's not how they do it.
(3) People get to access password hashes as soon as you mess up a single database query. The idea behind storing safe hashes is to prevent your stupid mistakes from screwing over every one of your users. The stupidest people of all are the ones who assume they aren't going to make stupid mistakes.
Security is an never ending war. It is evolving. And, there is always a way to crack whatever you want(it may take time). But, the question is: do you really have to waste so much time on secure something? Or, you can use your time to implement something useful.
You are right. Use whatever auth system which is available.
I think it's more of an issue of what is most pragmatic. Do you know how many hour-long conversations I've had on freenode about the best method to generate hashes? At the end of the day, most people are not targets of the Russian Mafia. And a lot of people are building something that might never get used by more than 50 people, so they don't care. If you're a Department of Defense contractor, I could understand why you would go out of your way.
I think it's fine if you block IPs after they've hit a fail threshold for logins. Or at least freeze the account for a certain period of time (see failed iPhone login attempts).
Think about what you're saying. "Security doesn't matter for these applications because they have almost no users, so we'll do something that will royally piss off the few users we're desperately trying to retain, and which will add no security. What's more, by implementing it ourselves, we'll pay extra to do that."
I agree with you. People talk about this stupid hashing thing far, far too much. Especially because there's already a "right answer". Just use whichever auth plugin is most popular and provides bcrypt.
You're correct that rainbow tables are trivially avoidable, but it shouldn't take a single stupid mistake to expose your database. Password hashing is a last line of defense, not something that should ever be necessary unless something has gone very wrong indeed.
It shouldn't take a single stupid mistake to turn a string copy into a passwordless remote software update mechanism. $3+Bn USD of "shouldn't", down the drain.
Can we talk about the real world, now? The reason Microsoft is driving modern offensive computing researchers nuts isn't that they got rid of the "stupid errors"; it's that they figured out how to make the runtime mitigate those errors with ASLR, NX, safe exceptions, and checked heaps.
In the real world --- and I am speaking from bitter and recent experience with very, very, very smart clients here --- you should assume you are going to make stupid mistakes, and do everything you can reasonably do to keep those mistakes from totally screwing over your customers.
"(2) There's a reason that no mainstream consumer application actually does this: as soon as you lock a normal user out of their account for an hour, you probably lose the user forever."
I don't know, it seems to work pretty well for Microsoft
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 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.
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.
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)
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.
That's what I had been using earlier, problem with that is the ugly login box the browsers prompt (there is no way to integrate HTTP Authentication in to HTML)
ah. well, I obviously have a very poor sense of aesthetics (look at my webpage- it would have been ugly in 1995) but thanks, that answers my question. (my question is "why don't people use http auth anymore?)
I really like http auth because it's a system level solution, and I'm the computer janitor; I know where the problems with http auth are without wondering if the dev who wrote the webapp made a mistake or not.
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.
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.
Anyway, if you don't use a nonce per user or a time consuming hashing method, then all tptacek's comments apply. His link in http://news.ycombinator.com/item?id=576021 is worth your time.
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.
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?
* 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.
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... 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.
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.
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.
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.
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.
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.
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.
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.
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