The web is nothing if not a maze of user accounts and logins. Almost everywhere you go on the web requires yet another new set of credentials. Unified login seems to elude us at the moment, so the status quo is an explosion of usernames and passwords for every user. As a consequence of all this siloed user identity data, Facebook and most other web apps encourage us to give out our credentials like Halloween candy, as Dare Obasanjo notes:
On Facebook, there is an option to import contacts from Yahoo! Mail, Hotmail, AOL and Gmail which requires me to enter my username and password from these services into their site. Every time I login to Yahoo! Mail there is a notice that asks me to import my contacts from other email services which requires me to give them my credentials from these services as well.
This is a deplorable state of affairs. We're teaching users that their credentials are of little value and should be freely handed out to any passing website that catches their fancy. It's an incredibly dangerous habit to inculcate in users; it makes them far more vulnerable to phishing:
If users get comfortable with entering their credentials in all sorts of random places then it makes them more susceptible to phishing attacks. This is one of the reasons services like Meebo are worrying to me.
The last thing we should be doing is coming up with ways to make phishing more powerful. Phishing is a social engineering exploit so timeless, so effective, and so powerful, I call it the forever hack. Rainbow table and brute force attacks can be defeated through judicious use of technology. Phishing can't.
Users collect usernames and passwords like they do Pokémon. It's a sorry state of affairs, but for better or worse, that's the way it is. We, as software developers, are trusted with storing all these usernames and passwords in some sort of database. The minute we store a user's password, we've taken on the responsibility of securing their password, too. Let's say a hacker somehow obtains a list of all our usernames and passwords. Either it was an inside job by someone who had access to the database, or the database was accidentally exposed to the public web. Doesn't matter how. It just happened.
Even if hackers have our username and password table, we're covered, right? All they'll see is the hash values. Only the most grossly incompetent of developers would actually store passwords as plaintext in the database, right? Right?
Recently, the folks behind Reddit.com confessed that a backup copy of their database had been stolen. Later, spez, one of the Reddit developers, confirmed that the database contained password information for Reddit’s users, and that the information was stored as plain, unprotected text. In other words, once the thief had the database, he had everyone’s passwords as well.
Wrong.
I'm only a mouth-breathing Windows developer, not one of the elite Y-combinating Lisp, oops, Python developers working on Reddit. And even I know better than that.
You might think it's relatively unimportant if someone's forum password is exposed as plain text. After all, what's an attacker going to do with crappy forum credentials? Post angry messages on the user's behalf? But most users tend to re-use the same passwords, probably because they can't remember the two dozen unique usernames and passwords they're forced to have. So if you obtain their forum password, it's likely you also have the password to something a lot more dangerous: their online banking and PayPal.
My point here is not to drag the good names of the developers at Reddit through the mud. We're all guilty. I'm sure every developer reading this has stored passwords as plain text at some point in their career. I know I have. Forget the blame. The important thing is to teach our peers that storing plaintext passwords in the database is strictly forbidden-- that there's a better way, starting with basic hashes.
Hashing the passwords prevents plaintext exposure, but it also means you'll be vulnerable to the astonishingly effective rainbow table attack I documented last week. Hashes alone are better than plain text, but barely. It's not enough to thwart a determined attacker. Fortunately, the kryptonite for rainbow table attacks is simple enough-- add a salt value to the hashes to make them unique. I provided an example of a salted hash in my original post:
hash = md5('deliciously-salty-' + password)
But IANAC -- I Am Not A Cryptographer. I meant this only as an example, not as production code that you should copy and paste into that hugely popular enterprise banking solution you're working on. In fact, I ripped it almost directly from Rich Skrenta's excellent post on using MD5 hashes as utility functions.
Thomas Ptacek, on the other hand, is a cryptographer, and he has a bone to pick with my choice of salting techniques.
What have we learned? We learned that if it’s 1975, you can set the ARPANet on fire with rainbow table attacks. If it’s 2007, and rainbow table attacks set you on fire, we learned that you should go back to 1975 and wait 30 years before trying to design a password hashing scheme.*We learned that if we had learned anything from this blog post, we should be consulting our friends and neighbors in the security field for help with our password schemes, because nobody is going to find the game-over bugs in our MD5 schemes until after my Mom’s credit card number is being traded out of a curbside stall in Tallinn, Estonia.
We learned that in a password hashing scheme, speed is the enemy. We learned that MD5 was designed for speed. So, we learned that MD5 is the enemy. Also Jeff Atwood and Richard Skrenta.
Finally, we learned that if we want to store passwords securely we have three reasonable options: PHK’s MD5 scheme, Provos-Maziere’s Bcrypt scheme, and SRP. We learned that the correct choice is Bcrypt.
* editor's note: Not quite true. Most "modern" software does not use a modern password scheme. Windows XP/2000/NT, phpBB, the majority of custom password schemes; all vulnerable to rainbow table precomputation attacks.
In summary, if we're storing passwords, we're probably storing those passwords incorrectly. If it isn't obvious by now, cryptography is hard, and the odds of us getting it right on our own are basically nil. That's why we should rely on existing frameworks, and the advice of experts like Thomas. What higher praise is there than that of praise from your sworn enemy?
Let's recap:
Of course, none of this guarantees you'll be able to prevent someone from deducing that Joe User's Myspace account password is "myspace1".
But when they do, at least it won't be your fault.
| [advertisement] PhotoDrop.com makes it simple to create and share online photo albums. Upload your full resolution pictures via the web site or with the free Photo DropZone utility, and you're done. No fees. No storage limits. It's the fastest and easiest way to share photos and create albums. |
Posted by Jeff Atwood View blog reactions
« Classic Computer Science Puzzles Practicing the Fundamentals: The New Turing Omnibus »
> So, the user's password, instead of being stored as the hash of "myspace1", ends up being stored as the hash of 128 characters of random unicode string + "myspace1".
Instead of 128 random unicode-characters you should go with 256 random bytes. Otherwise some implementations will lead to every other byte being zero (if the implementer just writes a random string of ASCII/Latin1-characters).
> Use a cryptographically secure hash.
Why only one and not more. All certificates you can "buy" for the usage in SSL-servers, ... use MD5 and SHA1. Using SHA256 instead of SHA1 should please even the most paranoic and performing a rainbow table attack on a system that is secured that way should be quite difficult because there might be passwords leading to the same MD5 or SHAx hash, but both at the same time it's very unlikely.
More interesting (and not answered here) is how to secure passwords that are used for authentication as client within a server (e.g. for data-transfer-protocols like OFTP). A hash doesn't work because the password is needed to be transfered (or used for the response of a challenge being sent by the server). And securing the password within a password-protected store leads to a chicken-egg-problem: How to secure the password being used for unlocking the store.
Best regards,
Lothar
lothar on September 17, 2007 04:55 AMPlease not 'nonce': http://en.wikipedia.org/wiki/Nonce_(slang)
I'm a former plaintext-password-storing web developer. A user called me out on this after a year of operation, and until that point I really didn't see the problem. It was an "oh-duh" moment.
It's easy to pick out the websites that do this, since they're the ones that will offer to e-mail your password to you if you forget it. The ones that only offer a password reset are likely to be adequately encrypting your information.
I'm not a cryptographer either, but when I salt the key, I use a couple lengthy constants of special characters, as well as some other constant data about the user. I can't imagine a pre-computed table big enough to contain every possible combination of 100 character keys.
Scott on September 17, 2007 05:26 AMI store the SA passwords to our databases on a post-it on the front of my monitor?
Is that incorrect?
Telos on September 17, 2007 05:37 AMIt's unfortunate that sometimes storing passwords as plain text is a contractual requirement from the client because they don't want to be forced to create a new one when they forget their original password. Sad world we live in.
sikander on September 17, 2007 05:41 AMOr don't store identity at all: http://identity20.com/media/OSCON2005/
Jim on September 17, 2007 05:57 AMAs long as you wrote it in RED ink, and not BLUE ink; you're fine.
Brandon on September 17, 2007 05:58 AMThe part you mention about how if somebody obtains a forum password it may be dangerous is very true, in the past I used an exploit in phpBB to gain administrative access to the control panel where I was able to take a backup of the forum and extract the admin's MD5 and then get it cracked, he was also foolish enough to use this same password as the one for the site's control panel...actually this happened more than once on a few different sites...but I never did anything malicious to the sites, fortunately for them, I hope they learned that lesson though.
Dana on September 17, 2007 06:00 AM@Brandon: What about black ink?
Telos on September 17, 2007 06:04 AMAs I user of many sites I find this helpful:
http://angel.net/~nic/passwdlet.html
Basic premise is this: Use a master password and the service url as salt, then hash it. Since your password is generated at the spot - not even you know what it is (nor should you care).
As far as you are concerned - you only need to remember your master password. The service gets a strong password they can store in plain text if they want.
Goran on September 17, 2007 06:04 AMWhile we're on the subject ....
And don't send me the password in my sign-up confirmation email. I know what my password is: Now everyone else can, too.
Trevel on September 17, 2007 06:08 AM"Add a long, unique random salt to each password you store"
I still don't quite get this - if the salt is random, how do we reliably generate the same salt value (For the same user) next time they log in?
Bob Armour on September 17, 2007 06:12 AMAfter reading your last password blog entry, I've been slowly changing my passwords to something a bit more arcane. Upon reading the password requirements for one of my banks, I found that their password rule is that they must be 6-9 case-insensitive letters and/or digits. I emailed them and told them that wasn't very good. They replied that since their web passwords and phone passwords were the same, that's the best they could do. They claim to be working on a solution. Mind you, this is a BANK.
David A. Lessnau on September 17, 2007 06:12 AMEven more scary is that some of the same developers who store passwords in plain text are storing your credit card information.
Years ago, after failing to talk a client out of storing credit card information in the first place, I did a lot of research on how best to secure it - knowing that his hosting environment was going to be, shall we say, inexpensive. Scary stuff, and not a responsibility I wanted.
Every time we code these things we're betting that no hacker on the planet is a better programmer than we are.
Jim on September 17, 2007 06:21 AMJeff you should respect the term 'Hacker'
[I prefer the term: 'Cracker' ]
If somebody has access to a password database then
what about the whole directory? Hashing the passwords is
less important than securing the local/web files.
A better solution than using a static salt (or in addition to using a static salt, even better!) is to salt the password using the username (or some the result of some function on it). Otherwise someone could conceivably determine the salt of your website and generate a rainbow table for all your users. If the salt is dependent on the username, a rainbow table is only good for one user.
Cryptography is really a euphemism for competitive paranoia.
David on September 17, 2007 06:24 AMThe facebook example is interesting: As facebook needs your gmail password to log into gmail on your behalf, it's no use them storing just a hash of it - they must need (at some point) the plaintext at every login. Obviously the passwords should be encrypted in the database, but I wonder if there's a smarter solution...?
Roddy on September 17, 2007 06:27 AM"And securing the password within a password-protected store leads to a chicken-egg-problem: How to secure the password being used for unlocking the store."
Not sure what the problem here is. Encrypt your store using a key derived from a given password. You don't need to store the password anywhere, you have the user input it, use it to try and decrypt the store. If the password was wrong, the store will fail to decrypt.
How do you avoid storing a password in plaintext when a password-recovery-service is required?
Jörn Zaefferer on September 17, 2007 06:31 AMSince I've started as a professional .NET web developer the first thing that gets written is the password page (After the pseudocode and specifications atleast). I'm working on a project now that uses a lot of different security techniques. We've put on layers and layers of security. And if we really wanted to we could make it even more secure. But all of that is wasted because the user has to be able to reset their password if they forget it. Which means that either the email or login field has to be unencrypted or easily decrypted in the database. I call this the 'leaving the key under the garden gnome' security.
But it will always be a compromise between security and ease of use until their is a better way to store passwords. And a better way to recover credentials if they are lost.
// My facebook habit of adding friends has stopped in its tracks until they offer another method for finding friends. Whatever happened to the email lookup that existed when it was for college alums and students. One thing I'm not doing is opening my address book for anyone.
Joe Chin on September 17, 2007 06:35 AMFor another example of "bad programmer, bad!":
I'm an URGE subscriber. Since they have "merged" with Rhapsody, they migrated my account information. Once, long ago, I had tried Rhapsody out, and had reverted to their free 25-plays-a-month plan.
Now, as Jeff said: no one can remember all 30 username/passwords out there. So, I reuse a few. I happened to have the same login for Rhapsody's old account and URGE's new account. So, when they migrated, and I try to login, I get the message:
"You have 2 accounts with the exact same username/password combination. Please call customer support."
Cheebus. I shudder to think what that stuff is stored in. Definitely not salty, probably very plain. Good for crackers.
Nicolas Webb on September 17, 2007 06:36 AMWhenever I store user passes I always use a salted hash, with the salt stored in the database and unique to each user (just to make it that extra bit harder for the cracker). Using a unique hash per user means that AT MOST the cracker can get one password out of his hash table, before having to change the salt and start again. What does concern me is sites where they say "Alpha-numeric characters only", or " Your password must be between x and y characters long" they just scream "I store my passwords in plaintext".
Heffocheffefer on September 17, 2007 06:46 AMYeah I've been at places where they view this as really a non-issue since the only access that could be had would be into our system (and therefore not a huge deal as those systems weren't really mission-critical). Management just couldn't see any reason to not store passwords as plain-text... but for one system in particular, I just went ahead anyway and chose a very basic symmetric encryption method that incorporated salt. Yeah it wasn't great and could be bypassed, but it was *something* and a lot better than plain-text!
The key point that management couldn't seem to get was that these users are so very likely to be reusing their passwords! I may have a max of 5 unique (some very secure, some not so secure) passwords that I reuse with all the various username+password accounts around the Internet and in work-related systems. I highly doubt that many regular users have up to 5 unique passwords though...
What about the security of browsers storing user-names and passwords (i.e. Password Manager or AutoComplete)? Specifically Firefox and IE... I heavily rely on Firefox and its Password Manager, but I didn't know until I checked right now that its encryption can be further tightened. How? [On Firefox 2.0] Here are the steps:
1. Tools -> Options -> Advanced tab
2. Encryption sub-tab -> Security Devices button
3. under "NSS Internal FIPS PKCS #11 Module", click "Change Password". Set a secure password (gotta have one more don't we??).
4. Click "Enable FIPS".
SecurityFocus has a great and in-depth article on this topic here that I was referring to on this: http://www.securityfocus.com/infocus/1882
Chris Harmon on September 17, 2007 06:52 AMIt seems you fell in love with the "salting strategy".
Beware! Now you have to protect the "salting key" too!
@Bob Armour - wow, you don't get it 3 times as much as you thought you didn't ;) However, I think the idea is that the salt is "randomly generated vs. a dictionary word" rather than "randomly generated per database row", otherwise there's definitely an issue - you have to store the salt generated for each row.
@Jörn Zaefferer, Scott - Hashing passwords definitely stops the system from 'remembering' your password, however there are many 2-way encryption techniques that use a private key to turn a recognisable phrase into something effectively random for storage and back again when required. HTTPS is a prime example of this. Of course, you then need to ramp up the security on the private key
Gareth on September 17, 2007 07:05 AMThat's the magic of it! You don't really need to protect the "salting key." Sure, it makes it harder if the attacker doesn't know it, but even if they do, they still have to crack the hash (by dictionary attack, or generating a new rainbow table), which is a non-trivial operation.
David on September 17, 2007 07:07 AM"It's unfortunate that sometimes storing passwords as plain text is a contractual requirement from the client because they don't want to be forced to create a new one when they forget their original password. Sad world we live in."
You still should not be storing in plaintext. You should be storing it using reversible encryption. That way if someone gets a backup copy of your database, as happened with Reddit, they still will not have will not have the encryption key you used.
John Rutherford on September 17, 2007 07:08 AMAlright, I'm sold on bcrypt. Anybody know of a .NET implementation? All Google's giving me is some Ruby module.
@Bob: You save the salt. It doesn't need to be a secret.
@David: You probably should actually read the preceding discussion, because a salt based on the username is far worse than a "static" salt, which in turn is far worse than a random one. Jeff said it right in this post - don't try to be clever.
OT: I thought it was pretty funny in the Skrenta post, where someone made a comment about how using an identical salt for every password wouldn't make the system much more secure, and he "corrected" his entry by putting the salt at the beginning instead of the end. Whoosh!
Aaron G on September 17, 2007 07:12 AMJorn:
2-way hash.
RG on September 17, 2007 07:12 AMBob Armour wrote:
"If the salt is random, how do we reliably generate the same salt value the next time the user logs in?"
You would have to either store the user-unique hash together with the salt used to generate it, or make the salt a function of some other piece of information unique to the user account, like the user name.
The SqlMembershipProvider that ships with ASP.NET uses a unique salt for each hash that it stores.
John Rutherford on September 17, 2007 07:18 AMyou know, I thought about using a random number as as salt.
in fact, I thought of using the unique installation ID for it, which is already stored in my database.
perfect.
but then I thought:
damn, if someone gets the database, he gets the unique installation ID along with it.
there goes the theory.
I'm not sure what the Facebook example has to do with password storage. They say right in the screen shot that they DON'T store passwords. It's a one-time login, so they can access your address book, and it's very convenient. How else would they do it? You're right that it's vulnerable to phishing though.
JW on September 17, 2007 08:01 AM@Arron G: Could you (or anyone else) elaborate on why the username would be a "far worse" choice for a salt than a "static" salt? I'm afraid I don't see why that would be the case since by adding a unique salt on to each password, a new rainbow table would need to be generated for each user (assuming unique usernames is a constraint). This seems to be better than having the same salt added. What am I missing?
Paul on September 17, 2007 08:06 AMSend this post on over the American Express... Most of the companies in my area that I have to use for utilities need a good lesson on web security. They don't allow special characters in passwords which drives me nuts -- alphanumeric passwords are horrible. I'm just thankful my bank allows me to use my 'secure' password.
Another good note for anyone who uses the same password for many sites: append the URL's acronym to the end of it. So, for example, if your password is "passwd" and the site is codinghorror.com, why not "passwdCH" :)
rob on September 17, 2007 08:14 AMI though a salt was stronger when containing some random bytes.
chmike on September 17, 2007 08:33 AM
In what circumstances does the password need to remain what it currently is if the user forgets it?
I really don't understand the 'email me my current password' functionality, unless of course you're in control of someone elses email account and want to know their password for other services but also don't want them to figure it out when a password changes, but is that really a feature?
@Paul: Aaron G's not exactly right on the whole "static salt"/password issue. A "static salt" isn't really a salt at all because it doesn't obscure identical passwords in a database any more than plain hashing does. Still, usernames aren't great salts as they tend to be short and use a narrow range of characters rather than the full character set range. Long strings generated by a high-quality PRNG are better. There's also the problem that if a user uses the same username and password on two sites, they both use the username as the salt, and they both generate their salted hashes the same way, it's immediately apparent to a cracker that they're using the same password on one site as the other. The thing about hashes is that their lossy. When the cracker uses rainbow tables to crack a hash, they don't necessarily get back the original string, but they might get back an equivalent string. If the two sites use the same salt, once the user's password is cracked on one, it's cracked on the other, but by using different salts, it has to be cracked for the other as all the cracker might have is a string which hashes to the same value as the password given a certain salt and hash.
Keith Gaughan on September 17, 2007 08:39 AM@Spoon: email isn't a secure medium. If somebody intercepts the email, they have your password.
@JW: It's a bit like this: would you give the keys to your house to somebody just so that they could get a number out of your address book? And you give your ATM pin to a random stranger so you could pay for book? What Facebook and sites like it who ask this kind of information from you are doing is just that. They should ask you for your friend's email addresses, not your first born child.
Keith Gaughan on September 17, 2007 08:47 AM"Add a long, unique random salt to each password you store"
I still don't quite get this - if the salt is random, how do we reliably generate the same salt value (For the same user) next time they log in?
Bob Armour on September 17, 2007 06:12 AM
The answer is to simply store the generated salt with the user pass.
For example I use PHP and MySQL and so a simple user information table only needs 3 fields: `username`, `pass`, `salt`
When someone tries to log in then you can check if the details are right by using the following:
$user = mysql_real_escape_string("USERNAME");
$pass = mysql_real_escape_string("PASSWORD");
$result = mysql_query("SELECT COUNT(`username`) FROM `table` WHERE `username` = '$user' AND `pass` = MD5(CONCAT('$pass', `salt` ))");
if(mysql_result($result,0) == 1)
{
echo "Correct details!";
}
else
{
echo "You silly hacker";
}
Hopefully that will clear things up for you.
Heffocheffefer on September 17, 2007 08:48 AMFor all you out there that are still wondering how exactly to store passwords in the database...
You don't. Try doing this as a minimum:
SIGN UP:
1) Create a user account record with a random salt in it
2) Put a password input
3) On the backend, handle the submission by getting the random salt stored in the user account record, appending it to the password, and then hashing the whole thing. MD5 or SHA1 or higher, both are too long to break I think. Oh, and try to be forgiving by not enforcing the case for the password, and just uppercase it ... i don't think users really appreciate having to remember the case!
LOG IN:
1) Get the random salt from the user account record, append to the password, hash and check against the db.
2) If it fails, please let the user know what exactly went wrong (wrong password or account doesn't exist).
3) optional - Set a timer to delay the next login attempt until 5 seconds have elapsed. But don't put a low limit on the amount of attempts! 20 attempts should be allowed.
FORGOT PASSWORD:
1) Enter your email, and the site should generate a new password for you, and put it in the email link. Why do you think the good sites never send you your old password? Because they don't know what it is!!
2) As soon as you log in, the site should prompt you for your own desired password.
Peace,
Greg
Keith: They do give you the ability to enter email addresses; they just also provide the import tool as a convenience. I don't really see it as giving my PIN to a stranger -- I have used Facebook for long enough to trust them.
This feature turns what could be a long process (log into gmail, export your contacts, arrange them into the right format, import them into Facebook) into a very simple one. For me it's worth the tradedoff. But it would be nice if there were a way to only grant them access to my addresses, and not my mail.
JW on September 17, 2007 09:20 AMThe GMail import is an interesting problem faced by almost every Social Network as they almost all now use libraries to make this easy. It's also faced by untold numbers of Twitter utilities and many other use cases. There are two lines of attack to deal with this which are related.
1) Come up with a formal API for exporting and syncing Social Graphs between Social Networks. See Brad Fitzpatrick. Then get it adopted widely.
2) Come up with a formal API equivalent to OpenID to allow a user to authorise Site A to make data requests to Site B on their behalf. See oAuth.
The problem is not Facebook's. They're just using a common bit of code to help the user find their contacts. And it's not GMail's. They're just providing a tool for a logged in user to export their contacts. Unfortunately, it's the industry's problem for not coming up with a secure method of doing what the user wants to do.
Julian Bond on September 17, 2007 09:35 AMStoring is one thing. I have seen my share of the bad decisions taken while storing the passwords in plain text. However, we have sites on the internet which do not allow strong passwords.
My personal experience with a payment gateway provider recently taught me that. Their rule was 5-8 characters with no special characters allowed. There is a screenshot on my blog: http://blog.gadodia.net/leading-payment-gateway-disallows-strong-passwords/
I couldn't come up with an 8 character password without a special character that would show up as strong on the Microsoft Password Checker.
Vaibhav Gadodia on September 17, 2007 10:03 AM@John Rutherford:
"You still should not be storing in plaintext. You should be storing it using reversible encryption. That way if someone gets a backup copy of your database, as happened with Reddit, they still will not have will not have the encryption key you used. "
Man we already went over this in Jeff's last post on encryption. The attacker will just get the key, too. If it's an inside job, this will be trivial. If not, and the attacker can't get the key, why aren't you securing your database backups the same way as the keys?
Just store the hash. Password-resetting works just fine. There is absolutely no reason to store passwords (sym. encrypted or plaintext) in a database.
Eam on September 17, 2007 10:10 AMIn any production system, especially systems with thousands or millions of accounts, you'll find a very high number of user names that are dictionary words. This is probably the most important reason not to use the user name as the salt.
If you're unlucky, the combination username+password or password+username is actually a dictionary word itself. You'd be amazed at how often this happens, and modern crackers all check for mutations, so passwords like "Bl4cKh0l3" won't be much more secure.
If you're extremely lucky, and none of these combinations appear in any cracking dictionary, you're still worse off. A cracker could simply create a dictionary or rainbow table combining two dictionary words, and they'd only ever have to do this once. It's not expensive to do - much cheaper than trying to brute-force 15 characters even once, and their dictionary is valid for your entire database.
It should be pretty obvious why "clever" schemes - like hashing the username before using it as a salt - don't afford much additional protection. Aside from the increased probability of collisions, a cracker can just hash every password in his dictionary and then follow the exact same steps. It doesn't take long to do this.
Just use a random string of 10 characters or so. It's really not that hard! It doesn't need to be cryptographically strong or highly randomized, though of course it should choose from a large character set that includes punctuation and special characters. The code to do this is about 5 lines long. If you're terrified of collisions then add the user name WITH the random string, but don't try to substitute it.
Aaron G on September 17, 2007 10:31 AMHey Now Jeff,
As always I enjoy reading your post. I'm interested in your thoughts of CardSpace & any similar technology as an improvement to this situation we have of many UID's & pw's.
Thx,
Catto
@Heffocheffefer:
Well you seem to have the database implementation details correct, but if you really want a secure program, I feel like I should tell you that PHP now supports parameterized SQL.
Even if you remember to mysql_real_escape_string() everywhere, it still has issues escaping multi-byte character sets, IIRC.
Eam on September 17, 2007 10:46 AM> The problem is not Facebook's. They're just using a common bit of code to help the user find their contacts.
That's interesting, because Facebook uses images with one-hour expiration tags to prevent anyone from screen-scraping their contacts. Which is exactly what they do to other services..
http://www.25hoursaday.com/weblog/2007/08/21/FacebookTheSocialGraphRoachMotel.aspx
> How do you avoid storing a password in plaintext when a password-recovery-service is required?
Why do we have to email the current password to the user? We can simply reset the user's password, and let them change it after they log in.
> if the salt is random, how do we reliably generate the same salt value
I was confused about this as well. You store the *hash* of the random, long salt in the user table right next to the hash of the password. It's not a secret, so you don't have to hide it.
Jeff Atwood on September 17, 2007 10:50 AM"You store the *hash* of the random, long salt in the user table right next to the hash of the password."
Why would we want/need to hash the random salt value? If it is completely visible and completely random then does it make sense to hash it before adding it to the password? Or am I missing something?
Matt on September 17, 2007 11:40 AM> > if the salt is random, how do we reliably generate the same salt value
> I was confused about this as well. You store the *hash* of the random, long salt in the user table right next to the hash of the password. It's not a secret, so you don't have to hide it.
I thought the main idea was that if the database is compromised that the salt would help protect the hashed passwords. If you store the hashed salt in the database, then the salt is no safer than your hashed passwords.
So this really presents a two simple scenarios.
1. Entire application is compromised, source, database, everything. In this case you're totally screwed and you need to roll over to a backup plan.
2. Just the database is compromised. If you store a hash of the random salt in the database it's not clear to me how you can determine what the original salt was so that means the algorithm is a simple concatenation. salt + password or password + salt. You cannot create a more complex algorithm than that unless you have a way of determining what the salt is. If you store the password plain text you're almost better off because now you need to account for the salt as a split string and the attacker has no way of knowing short of compromising the app how to shortcut his way past combining the salt and password.
Andrew R on September 17, 2007 11:46 AM>> if the salt is random, how do we reliably generate the same salt value
>I was confused about this as well. You store the *hash* of the random, long salt in the user table right next to the hash of the password. It's not a secret, so you don't have to hide it.
Are you sure about that? Since hashes are one way, how would you compare a randomly salted password with a provided creditential with out knowing the (unhashed) salt?
Nathan Bell on September 17, 2007 11:48 AM@Jeff "Short Flip" Atwood:
"I was confused about this as well. You store the *hash* of the random, long salt in the user table right next to the hash of the password. It's not a secret, so you don't have to hide it."
What are you talking about, baby? Either that hash *is* the salt, or you don't hash the salt. Either way, you need to store the actual salt (possibly encrypted [a]symetrically) in the database.
Eam on September 17, 2007 11:57 AM> Why would we want/need to hash the random salt value?
I guess I was thinking of storing hashes in both columns just for consistency's sake:
hashed salt, hashed password
And then we would compute the password hash by appending the hash of the salt instead of the salt itself:
pass = hash(hashed salt + password))
But you're both right-- it probably doesn't matter whether we use the actual salt or the hash of the salt.
Jeff Atwood on September 17, 2007 12:00 PMJeff,
Excellent post. One other point (perhaps you can add this to your post): Software developers should never, ever ever email passwords to users in plaintext.
A year ago I signed up with a website that provides a minor free service. As soon as I signed up, I received a confirmation email to the effect of "Thanks for signing up! Your username is ABC, and your password is DEF."
This meant that not only were they storing the passwords in plaintext, they were emailing it around in plaintext for anyone to see. I was astounded by their incompetence. (I sent them an email, but they never responded.) I had not even asked for them to send my password. Rather, they did this for all new sign-ups.
Of course, the username and password were also the same ones I use for my email service, so anyone with half of a brain could have intercepted this email and gained access to my email account (and other online services.)
You can criticize me for repeating user names and passwords, but I can't remember more than a few passwords without writing them down. I have another more-secure password for financial sites, and that's it. I refuse on principle to create a new password for every trifling website I visit.
The bottom line is that services should never send passwords via email. If someone forgets the password, they should instead send a password reset and require the user to enter a new one.
Robert on September 17, 2007 12:05 PMThere seems to be a ton of confusion about what a hash is. A few points people seem to be missing:
- It is not a secret value
- It does not make any one password less susceptible to a dictionary attack
- It makes the database as a whole less susceptible to large-scale attacks
Rainbow table attacks are basically a scan for the "low-hanging fruits" of the password database. They immediately reveal the insecure passwords, but typically will not crack the better ones. Salts prevent these attacks from scanning the whole table at once.
@Aaron G:
Very interesting point. The username/password combination being a dictionary word is fairly intuitive, but you're absolutely right that even using the hash of the username doesn't add much security.
Great post. I also made one recently about the subject.
Saving passwords in a database? No!
http://jtbworld.blogspot.com/2007/09/saving-passwords-in-database-no.html
@Robert
> Excellent post. One other point (perhaps you can add this to your
> post): Software developers should never, ever ever email passwords
> to users in plaintext.
>
> A year ago I signed up with a website that provides a minor free
> service. As soon as I signed up, I received a confirmation email to
> the effect of "Thanks for signing up! Your username is ABC, and your
> password is DEF."
I agree with the point you're making but it would be simple to implement a registration system that emails the user their credentials upon submission and then stores encryped or hashed values into the database for later use.
Andrew R on September 17, 2007 12:17 PMJust got an email from web 2.0 hopeful Joopz, giving me a free upgrade to premier status for one month, along with a helpful copy of my user name and password from a long ago registration.
We need a hall of shame for these people.
For those who still don't get it: a salt is not some kind of extra password. The entire world may know what the salt is. The salt is a way to lengthen the user password to such an extend that it will be undoable to create a generic rainbow table up front.
If you use a salt, a cracker will have to build a new rainbow table specially for that salt. I don't know if anyone ever tried to use brute force to retrieve one of their own passwords, but trust me, it's a pain, as long as the password is long enough. Building a rainbow table is basically doing brute force, but then you also store every combination you generate, so you can check other hashes with earlier attempts.
Of course, after a while a determined cracker will have generated a rainbow table for a specific salt also, so there the user specific salt comes in. If you choose this user specific salt well enough, a cracker will have use brute force for every single password you stored. Saving the results in a rainbow table is useless now, because the majority of the bits that were used to generate the password hash, are unique per user.
[tinfoil_hat] I guess only the NSA has the capacity and the use for such huge rainbow tables. And trying to fight the NSA isn't going to attract a lot of users, who, after all, only want to see your fluffy bunny dancing.[/tinfoil_hat]
[b]In short: you don't need to secure the salt, you only need to make it unique, lengthy and random.[/b]
sobani on September 17, 2007 12:51 PMAndrew R,
Absolutely. However, the fact that they emailed passwords in plaintext as a matter of course didn't inspire confidence that they were otherwise smart about password management.
Robert on September 17, 2007 01:01 PMNot storing passwords in plaintext is good in theory, but in practice it's not always possible.
For example, a mail server must be able to access plaintext passwords because the APOP and CRAM-MD5 authentication methods require the server and client to compute hashes based on a salt provided in the session.
TLS solves this, but TLS isn't always possible.
I'm sure there are more authentication schemes in the world that use the same principles. Until they go away, plaintext passwords won't either because plaintext is better then the alternative.
Thanks for posting on this topic! Funny coincidence, I was thinking on writing about the same issue and explaining to our customers how we chose to approach the problem. Well I did that anyway and used your blog posting as a good introduction. The technical details of our approach and philosophy are posted here in my blog entry. I would appreciate anyone’s feedback on our choices.
I think websites need to put more thought into this and MORE importantly MORE transparency. Let me know how you do what you do with my password and personal information. We as consumers should demand this.
One challenge we face is allowing password reset. During this operation we email the user a new randomly generated password for use. Of course this new password is open to snoopers until the user changes it. Sure, you could send the user a link to click on to put in a new password (and make it a temp link) as Robert suggested. However, this is open to snoopers as well. Effectively, if I can snoop the password I can snoop the link and choose your new password. Both solutions have the same problem. Any good suggestions on how to deal with password reset in a more secure fashion would be appreciated. Of course, we could resort to the user contacting us -- but we want to stick with a self-service based solution. Any good suggestions to this problem would be much appreciated?
Ooops. Didn't realize I didn't post my blog entry on this. http://blog.tribalpizza.com/2007/09/how-do-we-secur.html
Also of interest is OAuth, which addresses the problem of websites (eg, Facebook) asking for you credentials to other sites (eg, Gmail).
http://www.hueniverse.com/hueniverse/2007/09/explaining-oaut.html
--
Many luxury cars today come with a valet key. It is a special key you give the parking attendant and unlike your regular key, will not allow the car to drive more than a mile or two. Some valet keys will not open the trunk, while others will block access to your onboard cell phone address book. Regardless of what restrictions the valet key imposes, the idea is very clever. You give someone limited access to your car with a special key, while using another key to unlock everything else.
Everyday new websites offer services which tie together functionality from other sites. A photo lab printing your Flickr photos, a social network using your Google address book to look for friends, and APIs to build your own desktop application version of a popular site. These are all great services – what is not so great about some of the implementations available today is their request for your username and password on the other site. Using Twitter as an example, the site simple yet powerful API created a rich community of applications built on top of its platform. But in order for those applications to update your status on Twitter, they must ask for your password. When do so, not only you expose your password to someone else (yes, that same password you also use for online banking), you also give them full access to do as they wish. They can do anything they wanted – even change your password and lock you out.
--
I think the issue of password storage and security is also relatively difficult based on the fact that people want webmasters to be able to get their password for them.
I have a site where I stored all of the password encrypted (probably not correctly, but better than nothing). I kept getting calls from the clients that they had forgotten their password and wanted me to tell it to them.
I told them they would need to use the site's forgot password functionality because the passwords were encrypted in the database. This always caused problems because they didn't feel that I needed to go to such lengths to secure the password.
Eventually I won the argument of not storing the users password in plain text by simply refusing to store it in plain text. To this day they still ask me for that change.
Ryan Smith on September 17, 2007 03:05 PMAll this talk of HASH's and Salts is great, but I have a lingering question.
Whatever you choose to do, you have to send something when you POST your form to the server. If you are using a SALT + HASH, you would send your password in the clear in the form post. Obviously, not every site uses/needs SSL, so this POST is could be intercepted.
Am I missing something? You cannot do the hash on the client and then send the hash, as then all the cracker has to do is send a matching hash , as its no different than sending a password in the clear.
What is the best way to send a password to the server for it to do a hash comparison anyways?
"...probably because they can't remember the two dozen unique usernames and passwords they're forced to have".
I had two dozen unique account ten years ago. I don't keep count any more, but the number of accounts I have now is in 3 digits. I don't even bother writing down passwords, I just click "forgot password" whenever I need to log in again.
Richard H. on September 17, 2007 03:41 PMA bad example i recently encountered was on Blogger, it requires you to login with you're gmail account. Now, you and I may know Google has acquired Blogger so it is "probably ok". But how do I explain to my (grand)mother/father or any other not-so-techsavvy person about what fishing is and the dangers thereof when even mighty Google says it is ok every now and then.
Guido on September 17, 2007 03:44 PM"Add a long, unique random salt to each password you store"
I still don't quite get this - if the salt is random, how do we reliably generate the same salt value (for the same user) next time they log in?
You don't. That would defeat the purpose. You generate a random salt each time the user logs in.
Richard H. on September 17, 2007 04:02 PM@Richard H.
No, I think your wrong. Nobody really cares if the salt is public. As long as it increases the length of the hash to the point where a rainbow table would have to be several terabytes in size then the cracker would be faced with a huge amount of effort to generate the table, as well as store it.
You can store the salt as normal value in your database column, generated for each user when they sign up, like:
SALT | HASH | USERNAME
Then, when you receive the password (apparently, in the clear, as my above question asks, thats what I'm not clear about), you do this:
SELECT SALT, HASH FROM authenticationTable WHERE USERNAME=@UserName;
Then Super Psuedo Code:
Salt = GetFromSQLQuery("SALT");
Hash = GetFromSQLQuery("HASH");
If ((Salt + hash(password)) == (Salt + Hash))
{
User.IsAuthentic = true;
}
The Salt is simply to make the values in the hash column obscenely long from what I can tell.
Jon H on September 17, 2007 04:14 PMErrr, my above code should be:
If (hash(Salt + hash(password)) == Hash)
etc
Assuming you store your salt in the clear, and the hash column is a hash of the salt and the password, not a concanation.
At least, thats what I believe we should be doing here. Otherwise, if the cracker had managed to get this table, it would be very simple to deconacate the salt from the hash's, and then build a table.
Jon H on September 17, 2007 04:18 PMJon H.:
"Whatever you choose to do, you have to send something when you POST your form to the server. If you are using a SALT + HASH, you would send your password in the clear in the form post."
No. The server sends the salt to the client. The client appends it to the password and sends the hash OF THE WHOLE THING to the server. The server performs the same operation. If the two hashes match, the user typed in the right password (or a probability 1/2^256 accidental match).
Richard H. on September 17, 2007 04:31 PMRichard H:
But such a scheme requires the server to 'know' the correct password, so it will need to be stored in some reversible way, which is bad. Also, the hashing will probably have to be done in javascript, so it could very well be that just using ssl is as fast and safer.
Somejan on September 17, 2007 04:36 PMJon, I never suggested the salt was secret. On the contrary, it must be public or the user can't log in. What I said was you do NOT generate the same salt value each time the user logs in. If you did that, someone could record your login packets and play them back later to log themselves on as you. You do this:
The client asks to log in.
The server generates a long random salt and sends it to the client.
The user types in his password on the client.
The client calculates the hash of password + salt and sends the hash to the server.
The server calculates the hash of the password + salt.
If the two match, the user typed in the right password. If not, sorry.
You do not store the hash of the password + salt, that is pointless because it'll change each time.
Richard H. on September 17, 2007 04:40 PM"It's unfortunate that sometimes storing passwords as plain text is a contractual requirement from the client because they don't want to be forced to create a new one when they forget their original password. Sad world we live in."
Well, I'm sorry but it would never be part of any contractual requirement to store passwords as plain text. This is in violation of the Data Protection Act. If you want to store your passwords so you can get them back then use a reversable encryption which uses a key, such as BlowFish.
Never ever try to say that storing passwords in plain text files is a requirement. If the client tells you this, explain why it's not possible. Even if the client can read the passwords, this may be in violation of DPA also.
John on September 17, 2007 04:44 PMdisclaimer: I'm not a cryptographer, but I think I understand some of the basics of it.
With a rainbowtable, you basically precompute all possible hashes for a specific hash function and a specific range of paswords (character set and maximum length). It can work with any hash/one way function. Once you have this table, you can easily look up any hash value and read out the accompanying plaintext. The smart part of rainbowtables is in storing the tables in a very smart way that requires only gigabytes instead of terra/exabytes. Generating these tables is more-or-less equivalent to doing a brute force search of the range of passwords. The size of the table is, I believe, more-or-less proportional to the 'password space', the number of possible passwords there are within your charset and max length, and it also depends on the 'accuracy' of the table. (Read Jeffs previous post about them, it says each table can reverse xx percent of hashes of passwords within the range. Rainbowtables use probablistic methods.) (Although some math on the numbers on the rainbowcrack site show the relation password space table size isn't really proportional, so I'm probably saying some things about stuff I don't actually know enough about.)
Rainbow tables work equally well against any kind of hash function, be it SHA or rot13 (though with rot13 there are other, easier ways to 'crack' it). The only factor is if the tables for a specific hash function already exist, or still have to be generated. (Assuming nobody has gone through the trouble of building a rot13 rainbowtable, rot13 is actually /more/ secure against rainbow crackers than md5 or SHA.)
Since md5, sha, windows hashing scheme and the like are well known hash functions, the tables for those can be found for free on the net.
Using a salt with your hashing function, you basically create your own private hashing function, for which new tables will need to be generated.
Generating rainbowtables is fairly expensive, but as the worlds largest supercomputers are botnets, I wouldn't assume determined crackers won't do this if the information in the stolen databases is valuable enough, though the bar is quite high. (And, as far as I understand rainbowtables, its very well possible to parrallelize rainbowtable attacks.)
Users passwords are often not very complex, a dictionary attack will find a large percentage of passwords. Salts can be made as complex as you want, by just using a longer string of randomly generated characters. When using a global salt of sufficient complexity, an attacker will have to get to that salt as well, because it won't be feasible to do a brute force/rainbowtable attack on the database without it. If the attacker has access to the salt, he can then generate a new rainbowtable for the compromised database with the hash function + salt, and once he has done that, he can compromise basically all passwords in the database. (But building the table is at least as expensive as doing a brute force search against the database.)
Using a different salt for each password (which then has to be stored alongside the password) basically makes it harder to generate the rainbowtable, because the attacker now has to combine each attempted password with each of the hash values found in the database. I'm not quite sure if it will be as expensive as doing an individual brute force search against every entry in the database separately. Also, if you only use a global salt, the attacker can start generating the rainbowtable once he has that salt, but before he has the database, and then use it on any user account.
So, AFAIK, the most secure way is to use both a global salt and per-user salts, and keep your global salt stored separately from the database, so an attacker won't easily get to both of them. Though having just one kind of salt already keeps you safe from an atacker who can't generate a new rainbowtable.
Of couse, once an attacker has both the database and the salts, the most important thing standing between him and your password is your password's complexity, so use strong passwords!
Somejan on September 17, 2007 04:45 PMAnother thing to add to my previous comment. When developing for the web, NEVER and that is a definate repeat of the word 'never' put ANY encryption/decryption/hashing/security algorithm in a piece of javascript code on your html page. That is not what javascript was intended for and it's not even 1% secure.
All scripts involving security and passwords should execute server-side where the user has no way of seeing or tampering with them.
Giving someone your security algorithms is the same as giving them the password or even passing them a plain text file.
John on September 17, 2007 04:50 PM@Gregory Magarshak: No!
First: "just uppercase it" as a password input policy unnecessarily weakens user passwords. If someone doesn't want to remember the case in their password, they won't put it in.
Second: you should never return a message that says EITHER "account doesn't exist" OR "wrong password". All this does is allow people to find out if a particular user name is valid in the system! Bad! It should simply tell you "Bad username/password combination" or something to that effect.
Third: not allowing two logins within five seconds would annoy the crap out of me. I would stop using any website that had this function. I botch a password here and there, in which case I don't want to have to wait even one second to try again. Now I can appreciate what you're trying to do. But it's just as effective to lock out a user (or maybe better, an IP address) for attempting 1,000 logins in some time period (say, a day). No cracking effort should be likely to succeed in 1,000 attempts unless your passwords are ridiculously short.
CynicalTyler on September 17, 2007 04:53 PMSomejan, you are correct. In our case the passwords are assigned, users do not get to pick them. If you wanted to allow that, you would have to send a salt just for the password and store that, I guess that is what Jon was proposing. In that case I would suggest creating 2 salts, a stored one for the password as Jon suggests, and a temporary public one to prevent replay attacks.
Richard H. on September 17, 2007 04:54 PMJohn, could you explain what is wrong with doing the hashing client side? The server never sends the client any private data. It is irrelevant what the client does to the data, all validation is done server side. In fact, the client HAS to do the hashing to prevent sending private data.
I also disagree that "Giving someone your security algorithms is the same as giving them the password ...". MD5, SHA-256, Rijndal, et. al. are all public algorithms. Their security does not depend on knowing the algorithm. In fact, I would avoid any security product for which the algorithm is not public.
Richard H. on September 17, 2007 05:02 PM"John, could you explain what is wrong with doing the hashing client side?"
Im only saying don't expose algorithms. If you're using md5 or sha then that's all well and good. But either way, the more that happens in server scripts like cgi and php the better.
I don't see any need for javascript except for "interface polishing" as I like to call it. You know, rollovers and alike.
John on September 17, 2007 05:07 PMCynicalTyler: thanks for responding. I wrote my post with those views partly to elicit some kind of response.
The uppercasing -- it does weaken the password by 2x per letter, but still that's plenty strong. How are they going to crack someone's password which is one of (26+10)^8 possible passwords, if it's hashed? On the other hand, though, you won't have aggravated users who didn't realize that the caps lock was on when they put in the password. Is the increase in security so necessary that you would make people remember the case of their password?
Secondly: Yeah, I wondered what was so bad about confirming that a username exists in the system. But I think you're right -- there is no reason we should release that information. So "bad username or password" it is.
Third: No, I simply meant to introduce a tarpit -- so you can resubmit a request 1 second later, but you won't get a response until 5 seconds later. Change the 5 seconds to 2 seconds and you get the point... just put a tarpit there... but I guess yeah it's unnecessary since you're cutting it off at n daily tries.
ALSO -- the captchas these days can easily be solved, right? There are some good solutions ot prevent spam -- but that's for another post :)
Gregory Magarshak on September 17, 2007 05:12 PM@John,
I, like Richard, see the need for the server to send the SALT to the client, otherwise you will be sending either the password itself, or the hash of the password in the clear, which is easily picked up by someone intercepting the HTTP Post.
However, I am not sure how I would go about making that more secure. It seems to me like all this discussion is about server validation, it makes sense more or less. However, we are still sending either the password, or a hash of the password effectively in the clear. It makes me wonder if the only true security is using SSL.
Jon H on September 17, 2007 05:13 PMhttp://en.wikipedia.org/wiki/Crypt_(Unix) talks about some salted password system commonly used in UNIX-like systems.
The one currently used in Linux and some of the BSDs is the salted MD5 system mentioned on that page.
As Wikipedia puts it:
"First the passphrase and salt are hashed together, yielding an MD5 message digest. Then a new digest is constructed, hashing together the passphrase, the salt, and the first digest, all in a rather complex form. Then this digest is passed through a thousand iterations of a function which rehashes it together with the passphrase and salt in a manner that varies between rounds. The output of the last of these rounds is the resulting passphrase hash.
The fixed iteration count has caused this scheme to lose the computational expense that it once enjoyed. Variable numbers of rounds are now favoured."
(The article does not explain how you would determine the variable numbers the last sentence refers to).
If you ever look at a hashed unix password that uses this system, it looks something like this: $1$RsDzxp4K$y6ARJtx/TRDYnPWp.3vGy/
Which is broken down as follows:
$1$ = Tells the crypt() system call that this is using the algorithm mentioned above
RsDzxp4K = The salt
$ = salt/password delimiter
y6ARJtx/TRDYnPWp.3vGy/ = password hash
(for the record, the password was "test")
Richard,
What you're doing is essentially duplicating (to a lesser degree) the conversation that takes place during SSL negotiation. Running the login / register page over SSL allows you to bring the focus of your approach back to the server (since you've secured your login packets), where you can execute code in an environment you can trust.
FWIW, I've always used a per-user hash combined with machine-specific reversible encryption, such as DPAPI.
Josh on September 17, 2007 07:42 PMIt's fun to blithley throw around absolutes like "never store plaintext passwords," but in the real world it's rarely that easy.
I'd estimate that more than half of commonly used authentication protocols (e.g. APOP, HTTP Digest, CRAM-MD5, etc.) require storing plaintext passwords on the server side. These protocols have in common the ability to authenticate a client without either side sending the plaintext password over the wire. This is good, since the wire is typically easier to attack than the database.
It makes more sense to qualify that sentiment as "never store plaintext passwords when the client is expected to send plaintext passwords over the wire during authentication."
The Authenticator on September 17, 2007 08:05 PMIn the database:
Username: rahrah
Password: (rah34rah)
- stored as hash of (Password + Salt): lkkjsdlfjlsdkjfs98345
Salt (hashed): asjodfj$%^insdjfhkidsjf
------------
Database compromised - attacker has 'salt', 'password', and 'username'.
Generates rainbow table of say - 14 chars aA-zZ-0,9-!@#$%
generateTable(14,$charSet,$hashType,$salt);
As the program runs it does the following:
for i=1 to 14 (14 chars)
for j=1 to $numOfUniqueChars($charSet)
$newPass = generated combination of $charSet
$newHash = $hashType('salt' + newPass)
storeKey(newHash, newPass)
---
Iteration finally comes to rah34rah, hashes that with the salt, stores key Hash with value rah34rah, and your password is cracked.
======================================
Where is the POSSIBLE benefit of salting a password and storing the salt alongside the user/pass? I brought this up last article about rainbow tables. Am I wrong or is this how it would go? It seems fairly simple to me, so please point out where this is wrong? A cracker only needs ONE password. Granted, only one password could be cracked at a time - but who needs more?
And if that nasty Storm bot-network wanted to contribute to generating multiple rainbow tables?
Posted too early..
Salts aren't designed to protect a single password, they're designed to protect an entire database by increasing the time cost of generating a table for each individual user. You can crack any password you wish (which isn't too strong for a dictionary attack). But once again, do you need more than one password? Especially if you see a nice little username called "admin" or "administrator".
`Josh on September 17, 2007 08:41 PMIn Jan I was at a company meeting. During the presentation, the dufus who put up some wiki service told us that the back of the room had cards layed out with our username/passwords. I looked at mine and it was my generic password that I use (I had logged in and changed my pwrd prior to the meeting).
So I go to bitch to them, and about 10 other people that this idiot has peoples passwords sitting on the table for anyone to look at. The response I got was "Well, it's just for this service." Nobody could grasp that people will re-use passwords and this may give them access to their network or other services.
I finally found one of the directors who went and removed them from the table. I got let go 3 months later. I hope the company burns in hell.
Me on September 17, 2007 10:30 PMHmmm. I wonder how our project stores passwords. I better start asking around now. Security is everybody's concern.
jon on September 17, 2007 10:49 PMWhile I admit that all these cross-site functionalities that require one to tell site A what their creds are for site B are open to phishing attacks, as outlined, if site A hashes the password provided for site B, how is site A supposed to make use of those credentials to interface with site B? Site A should encrypt the credentials somehow, in a reversible fashion, so that the credentials can be used repeatedly (assuming that repeated access to site B is part of the feature, for continuous updates) -- hashing won't provide that.
The real fix for this is that sites need to stop being walled gardens and need to encourage cross pollination by providing features to allow users to designate other people/sites access to their data, and need to provide APIs that can be used to programmatically interface with other sites to avoid having to "login" as a user to access that user's data.
Andy on September 17, 2007 11:23 PMGoogle's AuthSub is a nice solution to some of these cross site issues. I found it easy to authorize and import Google Docs without seeing a user's credentials or resorting to the 'benevolent phishing' maneuver.
http://code.google.com/apis/accounts/AuthForWebApps.html
Unfortunately it's not supported for all of their APIs yet.
Jeff on September 18, 2007 06:40 AMI believe you've made a cardinal mistake, and in fact, your md5('deliciously-salty-' + password) (md5 can be replaced with SHA-2 or any other hashing algorithm) is vulnerable to the very same rainbow table attack!
With a fixed salt (the 'deliciously-salty-') part, one can compute a rainbow table for that algorithm and that salt, and crack any arbitrary password (with the larger the table, the more classes of passwords that you can crack).
The correct way to store passwords is to store two values for each user: a salt (which is a randomly generated string of any length, the longer the better to a point), and hash(salt + password). That way, you cannot compute a rainbow table, and cannot crack the password.
As for the speed of a hashing algorithm being a vulnerability, you may be wrong. The security of the algorithm is the important part. md5 is a technically broken algorithm, and SHA-2 is technically better, but we are talking about security experts here - their idea of broken is different than ours. For all practical purposes, especially in this use case with the combination of random salt, md5 is still secure. See http://en.wikipedia.org/wiki/Md5#Vulnerability
For example, for www.goruneasy.com, I used this md5(random-salt + password) method. The salt is a 10 character string, using characters where each character's ASCII value is between 1 and 253, creating an incredibly large potential space. I don't believe, unless a truly massive vulnerability is found in the md5 algorithm, that this approach can be compromised.
It's too bad there isn't a generic authority-granting API. Various sites have hand-rolled ones, like Flickr and Facebook itself. For instance, if you want to use the Facebook Firefox extension, it kicks you off to Facebook, where you enter your password on the Facebook site, and then you allow the extension authority. No password ever given to the extension.
The obvious problem with custom APIs is that each provider needs to write one, so if GMail doesn't have one, you're screwed. And you need to write custom code to talk to each one, which is no fun either.
smackfu on September 18, 2007 08:34 AMFor those interested I wrote (read: patched together) a Rails plugin that handles authentication using Coda Hale's bcrypt library:
http://acts-as-authentable.googlecode.com/
Eivind Uggedal on September 18, 2007 09:14 AMSalting password is not neccesary, an encryption algorithm like SHA is enough.. and offcourse a minimum length for the password.
Tjerk on September 18, 2007 09:33 AM@Tjerk
"Salting password is not neccesary, an encryption algorithm like SHA is enough.. and offcourse a minimum length for the password."
You might want to reconsider. Any sort of hashed one way encryption can be cracked by finding any string that reproduces the same hash. While time consuming, it is not difficult.
Salting makes the generation of precomputed hashes difficult by increasing the size of the stored hash. A good salt means that a rainbow table to crack it would go from 3-4 gigs (for a simple SHA hashed password) to several terabytes in size.
Jon H on September 18, 2007 10:20 AMquote from Josh:
---------------
for i=1 to 14 (14 chars)
for j=1 to $numOfUniqueChars($charSet)
$newPass = generated combination of $charSet
$newHash = $hashType('salt' + newPass)
storeKey(newHash, newPass)
---
Iteration finally comes to rah34rah, hashes that with the salt, stores key Hash with value rah34rah, and your password is cracked.
======================================
Where is the POSSIBLE benefit of salting a password and storing the salt alongside the user/pass?
---------------
given a simple charset with 62 chars (alfa up and low + num):
62^14 = 12,401,769,434,657,526,912,139,264
now lets say that mister nasty cracker can compute 10^15 hases per second. Than it would still take 541.6 YEARS to compute this table.
Lucky for mister nasty cracker, he can already find tables without a salt on the net, so it will take him only the download time of the table.
Oh wait, you salted your passwords with 1 long salt for the entire db? Now mister nasty cracker needs those 541.6 years to generate a table for just your user account db.
What you said, you salted the passwords with an unique salt per user? ... Dang, better get a HUGE botnet first.
sobani on September 18, 2007 11:02 AMquote from Josh:
---------------
for i=1 to 14 (14 chars)
for j=1 to $numOfUniqueChars($charSet)
$newPass = generated combination of $charSet
$newHash = $hashType('salt' + newPass)
storeKey(newHash, newPass)
---
Iteration finally comes to rah34rah, hashes that with the salt, stores key Hash with value rah34rah, and your password is cracked.
======================================
Where is the POSSIBLE benefit of salting a password and storing the salt alongside the user/pass?
---------------
given a simple charset with 62 chars (alfa up and low + num):
62^14 = 12,401,769,434,657,526,912,139,264
now lets say that mister nasty cracker can compute 10^15 hases per second. Than it would still take 541.6 YEARS to compute this table.
Lucky for mister nasty cracker, he can already find tables without a salt on the net, so it will take him only the download time of the table.
Oh wait, you salted your passwords with 1 long salt for the entire db? Now mister nasty cracker needs those 541.6 years to generate a table for just your user account db.
What you said, you salted the passwords with an unique salt per user? ... Dang, better get a HUGE botnet first.
sobani on September 18, 2007 11:02 AMwoops, my apologies. I didn't knew my first try got trough already. Please remove the first try (and this reply)...
sobani on September 18, 2007 11:10 AMYes, this very issue started with me when Word 2007 came out with its clear-text-password Blog-posting feature. Sad.
Bryan Wilhite on September 18, 2007 12:02 PMIt's pretty spectacular how nobody really understands salts.
The point, people, is so that you can invalidate pre computed hash values (rainbow tables) by pre-pending a constant but random string in front of your password. It doesn't have to be hidden, it doesn't have to be random each time. It just needs to be somewhat longer than the clear texts used to create rainbow tables. If a user has a 'password' for a password, cracking it will take the duration of a a seek and scan in a DVD full of precomputed hashes. If a user has 128 bytes of garbage + 'password' as their password, chances are the DVD won't have the answer.
I wonder if anyone reads this far down. I know I didn't...
Mike on September 18, 2007 12:07 PMSomething I haven't seen mentioned is row-level encryption of database fields. That protects you from some attacks and not others, of course, and it's a high-end database feature... but when you have it it makes sense to use it. (And you can always change your database backend code to use it at a higher level - but that's slow, and an enormous pain to keep synched between backends, especially if you work in several different languages, to say nothing of when you have inline SQL queries.)
Foxyshadis on September 18, 2007 02:55 PMAs an aside - I'm quite glad that I don't even know any of my passwords anymore, aside from the one I use for throwaway sites. It's amazing how well these password hash systems work. (The downside is that it becomes very hard to change passwords short of complicating the scheme more than I'd like.)
Foxyshadis on September 18, 2007 03:10 PMWhen requesting a password reset, the password for cox cable emails is the word password. You would be surprised at the amount of people dont ever change it. When reseting passwords never use a default.
brian on September 18, 2007 06:12 PMThis is the way I've always felt about a user's trust online - if it has a pretty layout and stock models appear to enjoy using the website, the user will trust the website with any information it asks for. Sadly, this applies here - people think that FaceBook is some invulnerable application because it's large, when these are exactly the types of things that are under such great scrutiny.
FiSh on September 18, 2007 09:18 PMI gave up reading the conversation since it is essentially the same one we had with Jeff's last posting on password storage. I know I brought up a couple points which with further reflection turned out to be incorrect (Not sure what I was thinking). No need for details though, since obviously the conversation continues.
The logical and mostly correct outcome of the discussion is good, stored password hash + random salt = good security. Question is, will this design withstand the test of time.
Everyone is aware of the botnets, and the amount of spam being put out, which is almost an entirely seperate issue. I'm just curious if anyone else is scared to death that these botnets could be turned against our authentication systems themselves. You have a generally accepted good security practice, HASH + random salt stored in database, how long do you think this can stand up against a million or more PC botnet?
What is the targetted lifespan of your system?
What if your end user uses the same password for 10 or more years?
I have nothing against a good password scheme, but rule number 1 should always be, don't give away your database. Your system security architecture should be so good that the "evil" bad guys are unable to obtain your database from forgetten backups and all the other methods. The true reason we need to secure the passwords in the database, is we're not sure we can keep the data safe, and it's also very easy to implement rudimentry protection, that as it stands can discourage your script kiddies.
Rudimentry protection will not always be enough.
Kevin Nisbet on September 18, 2007 10:08 PMDepending on what you actually want to do with the password, you may *have* to store it in plain-text. Most CHAP implementations require this, for instance. What does using CHAP get you? You're not asking the client to send his secret over the wire. This is obviously a good thing. Other examples of security protocols that depend on both parties sharing a known secret (e.g. a password/username pair) with similar characteristics can be easily found.
For web development -- in particular, for implementations of a classic web "login" service -- here are some symptoms that may indicate that you probably need a plain-text password somewhere:
*) One of your requirements is to offer a "password recovery" feature
*) The secret is going to be re-used for other types of authentication (think "Single Sign-On") that require plain-text secrets
*) The secret may need to be shared with other partners who are unwilling or unable to deal with using your authentication service
@Tjerk
>Salting password is not necessary
Given the information presented so far in this thread, you're right. So far, salts have been discussed as a mechanism for making the password longer, as a way to defeat rainbow tables (and brute force, for that matter). In this perspective, salts buy you nothing that a longer passphrase wouldn't buy (except possibly somewhat higher entropy).
The real reason for using salts is not as a password extender (that can be solved with longer passwords) - it is as a password mutator.
The reason that salts are used is that users reuse their passwords between systems. If I crack your password on one site (finding an input string that produces the same hash) I can now log in as you on all other sites that use the same hashing function and where you have used the same password. Unless, that is, the password has been salted differently on all those different sites. Then I could only log in to those other sites as you if I actually have your plain text password, which is not what I get by cracking a hashed password.
Thus, you could acheive the real purpose of having a salt by using the salt value to mutate the password in some way without even making it longer. You could even make the password shorter. The important part is that, even if I use the same password on all sites, my password will be mutated differently on all sites, so that someone who has found a string that results in my hash on one system won't be able to reuse it on another system.
Of course, mutating the the password so that it becomes shorter would have the negative side effect of making the password easier to crack, so that is obviously not the suggested approach. Correspondingly, if you mutate the password in such a way as to make it longer, you make it a little harder to crack as a bonus. But this is a bonus, and /not/ the primary reason salts are used.
I've just written a blog post trying to explain this difference and why, in my opinion, storing passwords in plain text should be illegal.
http://www.matshelander.com/wordpress/?p=73
/Mats
Mats Helander on September 19, 2007 04:17 AMAppending (as is my habit...)
This perspective (salts are primarily for mutation rather than extension) also allows you to understand why early salts were often pretty short.
It is not the case that the makers of those early systems thought a couple-of-bytes salt would be enough to /extend/ the password in a meaningful way, making it more immune against brute-force attacks. Instead, they correctly recognized that it would be enough mutation to serve the primary purpose of using a salt as a mutator, preventing reuse of hacked passwords between systems.
Since then, salts have grown as this allows the secondary bi-effect of making the passwords longer and harder to crack to be exploited - which is, of course, all fine and good.
The problem comes when the primary purpose is forgotten and someone correctly observes that using longer passwords would then make salts unecessary - that's true with regards to the secondary effect of making passwords longer, but it is not true with regards to mutating passwords between systems.
/Mats
Mats Helander on September 19, 2007 04:34 AMIf you are using .NET, you may find the SecureString class to be helpful in storing passwords. SecureString keeps sensitive data encrypted in memory so that it cannot be easily stolen. SecureString plugs a specific security hole but does not guard against all threats while securing information in applications.
http://www.devtopics.com/securestring-safe-from-forensics-but-not-surveillance/
DevTopics on September 19, 2007 06:02 AM> Not storing passwords in plaintext is good in theory, but
> in practice it's not always possible.
In practice it is always possible. I know of no mail client that does not support either TLS or SSL connections; there has not been a need to support plaintext authentication for mail relay for some years now.
And if you tell me that "I have a user that will only use a particular piece of broken software", the answer to that question is "plaintext passwords are no longer an acceptable security risk in today's internet, sorry."
You can't use telnet for remote administration any more. You ought not to be using unencrypted POP to get your mail, or unencrypted SMTP to send your mail.
Regarding salt, Mr. Helander explains correctly.
Pat Cahalan on September 19, 2007 04:58 PMOne problem with hashed passwords: challenge-response authentication?
Sickboy on September 20, 2007 01:45 AMWhy would you not list account lock-outs as a defense against brute force attacks?
It's really simple, Joe User puts his password in wrong 3 times and his password is locked, changed, and he is sent an e-mail telling him to come set a new password or call customer service to create a new one, then let the brute attacker into a honeypot with a whole crapload of tantalizing information while you log the evidence.
Mattkins on September 20, 2007 11:36 AMMattkins,
Normally a brute force attack does not assume that the hacker tries passwords out via the login mechanism. It concerns the case when the hacker already has your hashed password, knows the hashing mechanism used and the salt, and is now down to running different combinations of characters (brute forcing) against the hacker's own installation of the hash function until a string producing the correct hash output is found - not the login page on the system.
/Mats
Mats Helander on September 20, 2007 12:35 PM> It's really simple, Joe User puts his password in wrong 3 times and his password is locked, changed,
An attacker is likely to convert your account lockout setting into a denial-of-service attack, by locking out every account on the system. And a scheme like this is a massive burden on the helpdesk.
Jeff Atwood on September 20, 2007 03:29 PMTheDave>>"Not storing passwords in plaintext is good in theory ... access plaintext passwords because the APOP and CRAM-MD5"
Side stepping the discussion slightly --
Both APOP and CRAM-MD5 are horribly insecure... You are only fooling yourself if you deploy a security solution based on these protocols.
If you read POP3 "Security considerations", it is painfully obvious that no one even took five minutes to think about what to write there. It is just a short section of wishful thinking.
http://www.ietf.org/rfc/rfc1939.txt
The APOP way of doing things known to be insecure in 1995,
http://citeseer.ist.psu.edu/433722.html
... why POP3 RFC fail to mention this, or why APOP hasn't been deprecated yet, who knows?
APOP being being way too insecure has been discussed since at least 2001, and those discussions didn't even include MitM nor rainbow tables. I wager the knowledge about this was known far longer back by the experts.
Today when rainbow tables are being deployed, digest authentication without additional security mechanisms are a laugh.
http://www.ietf.org/rfc/rfc2069.txt is an excellent reference of how to push digest authentication from "horribly insecure" to "good enough for our needs";
- Quality of Protection modes introduce client nounce and client counters into the scheme digest to stop choosen plaintext attacks (however, the vulnerability will continue to exist until browsers stop MitM attacks by rejecting any
- Servers can choose if they want to store cleartext or HA1 hash.
How about FIRST protecting your system enough to avoid people to get the hashes??
Nicolas on September 29, 2007 11:54 AM"Do not invent your own "clever" password storage scheme"
Ok, I agree. So what's a good hash method to store passwords with PHP?
hash() with sha512 [and salt]? good enough?
PSilva on September 29, 2007 08:38 PMI wasn't able to find a managed .NET implementation of BCrypt, so I ported jBCrypt (using C#). Hopefully some of you find it useful.
Derek on October 2, 2007 09:02 PMGreat work, Derek - I already blogged about your solution.
Adi on October 7, 2007 02:51 AM> It's really simple, Joe User puts his password in wrong 3 times and his password is locked, changed,
>An attacker is likely to convert your account lockout setting into a denial-of-service attack, by locking out every account on the system. And a scheme like this is a massive burden on the helpdesk.
A middle solution may be to temporarily (say one minute) lock out the user with the appropriate message.
Adi on October 7, 2007 02:54 AMi forgot my password pls help me what should i do
manuel on October 18, 2007 06:35 AMWe don’t need to store users’ passwords at all. We must identify a user by SSL client certificate or a PGP signature, it is more secure.
beroal on October 23, 2007 05:14 PMNobody has mentioned one time passwords yet. Seems odd?
SSL certs are great in conjuction with a password but not really the answer. For a reasonably secure site, just go for SSL between client and server (no sniffing, spoofing, man in the middle, proxying etc) and use a password + salt combo in the DB with a password complexity requrement.
If you want to take it further, use pin + one time passwords with SSL and spend some money on RSA, Cryptocard etc.
adrian on January 14, 2008 07:25 PMBob Armour wrote:
"If the salt is random, how do we reliably generate the same salt value the next time the user logs in?"
Bob - This is how I do it:
(for example purpose, I'm use plain text)
salt = asaltvalue (this will be a random string of fixed length)
password = apassword
password hash = sha1(salt+password)
password value to store in DB = password hash + salt
Conceivably, if someone were to see the password in your DB, they wouldn't know if the salt is part of the password, or if the password is just a hash of something. Even if they had some idea that the salt is part of the password, they wouldn't know the salt length. So in effect, your masking the salt in the password. To compare input password with password in DB, do:
password in = apassword
salt = last 10 characters of the password value in DB
password hash = first 40 characters of the password value in DB
is sha1(password + salt) = password hash?
YES -> do something
NO -> do something else
| Content (c) 2008 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved. |