views:

541

answers:

7

In a web application written in Perl and using PostgreSQL the users have username and password. What would be the recommended way to store the passwords?

Encrypting them using the crypt() function of Perl and a random salt? That would limit the useful length of passswords to 8 characters and will require fetching the stored password in order to compare to the one given by the user when authenticating (to fetch the salt that was attached to it).

Is there a built-in way in PostgreSQL to do this?

Should I use Digest::MD5?

+13  A: 

Use SHA1 or SHA256 hashing with salting. Thats the way to go for storing passwords.

Henri
Also remember to salt your passwords. http://www.aspheute.com/english/20040105.asp
Dan Herbert
Please make sure you don't need to support authentication schemes based on hash. I regret my decision to hash the password in a database due to new requirement to support LDAP {SSHA} scheme.
ZZ Coder
@ZZ Coder: and why is that a problem? Just create your new set and when a user is logging in you do some behind-the-scenes magic to upgrade her in your new database. You should *never* store passwords that can be retrieved in plain text and hashing is a good way to do it.
Leonardo Herrera
bad idea. See the other posts in this thread, relating to the speed of brute force attacks on your passwords.
dland
@dland, see my comment there. Speed is really a non-issue.
Henri
+1  A: 

I would suggest storing it as a salted md5 hash.

INSERT INTO user (password) VALUES (md5('some_salt'||'the_password'));

You could calculate the md5 hash in perl if you wish, it doesn't make much difference unless you are micro-optimizing.

You could also use sha1 as an alternative, but I'm unsure if Postgres has a native implementation of this.

I usually discourage the use of a dynamic random salt, as it is yet another field that must be stored in the database. Plus, if your tables were ever compromised, the salt becomes useless.

I always go with a one-time randomly generated salt and store this in the application source, or a config file.

Another benefit of using a md5 or sha1 hash for the password is you can define the password column as a fixed width CHAR(32) or CHAR(40) for md5 and sha1 respectively.

hobodave
MD5 has been considered unsafe for some time. SHA1 or SHA256 should be used
EKS
Eh, I've read those blog postings too. For 90% (arbitrary) of use cases md5 is more than adequate.
hobodave
Yes it is, but why to use md5 if sha1 works the same, and provides a little bit more security? There is no point in choosing md5 above sha1 except if you have limited storage space or computational power.
Henri
@Henri: true, but there's no evidence presented either way as to his security, storage, or computational power needs. As a non-native postgres user, I merely grepped the manual and saw that it had a native md5 but no sha1, thus my recommendation. My suggestion warrants these comments, I agree, but a downvote? Meh, seems a bit much.
hobodave
MD5 may be broken, but the attack (by Xiaoyun Wang and Hongbo Yu) doesn't address the salted MD5 case. It assumes full freedom to pick the second input. Hence, the proposal is reasonable, and not trivially attackable. The only thing I'd do is use a less-trivial salting algorithm. Even something as simple as suffixing the password _length_ to the MD5 input would greatly complicate attacks.
MSalters
I don't see how being able to use a fixed with char field in itself is a benefit... It's more of a side-effect.
Magnus Hagander
Salt absolutely does *not* become useless if your database is compromised - that is, in fact, the only time that it provides a benefit. Not salting your passwords (or using the same salt for every user) means that an attacker can immediately determine whether two users have the same password or not. Failure to use salt also makes you much more vulnerable to attacks by lookup in a rainbow table rather than requiring an attacker to actually spend time computing hashes to find a match.
Dave Sherohman
@Dave I was referring to the specific case where the user table has a 'salt' column as well as a 'password' column. In this case if read access to the table were gained, it's procedurally trivial to crack the passwords using bruteforce, albeit not computationally trivial. If the salt is a value stored elsewhere then a brute force approach cannot be taken, as one of the inputs is completely missing. You would be able to see passwords that were identical however. IMO this is a result of not using a password checker that can detect dictionary words or all around "bad" passwords.
hobodave
... specifically, if two users have the same password chances are it's something stupid like s3cr3t69
hobodave
@Magnus: it is widely known and proven that fixed width rows perform better than a varchar row. Please google.
hobodave
@hobodave: Amazing! I use the same password on my luggage.
dreamlax
@hobodave the question was specifically about PostgreSQL, where this is not true. In fact, it can be (probably unmeasurably though) slower. Please read the source if you don't believe me.
Magnus Hagander
A: 

If you don't use a password recovery mechanism (Not password reset) I think using a hashing mechanism is better than trying to encrypt the password. You can just check the hashes without any security risk. Even you don't know the password of the user.

Chathuranga Chandrasekara
Given that he mentioned Perl's crypt() function, which "Creates a digest string exactly like the crypt(3) function in the C library", I suspect he/she knows they should use a hashing mechanism and was just using the wrong terminology.
David Precious
A: 

Using MD5 would be one of the easiest ways to accomplish your goal.

Flugel_BE
MD5 is considered broken, use sha1 or sha256.
Henri
stay far away from secuirty tags on stackoverflow.
Rook
+1  A: 

The pgcrypto module in PostgreSQL has builtin suppotr for password hashing, that is pretty smart about storage, generation, multi-algorithm etc. See http://www.postgresql.org/docs/current/static/pgcrypto.html, the section on Password Hashing Functions. You can also see the pgcrypto section of http://www.hagander.net/talks/hidden%20gems%20of%20postgresql.pdf.

Magnus Hagander
I've implemented pgcrypto recently, and it's pretty annoying to get working on your database (and you have to be real careful about your queries that use it, which if you format them in certain ways, can really slow execution). You have to install the module, run a script as root, and only then can you use `crypt`. it's secure, but not easy to add.If you have a good code-level hash algorithm, that may be a good alternative.
Tchalvak
+8  A: 

MD5 is commonly used, but SHA1/SHA256 is better. Still not the best, but better.

The problem with all of these general-purpose hashing algorithms is that they're optimized to be fast. When you're hashing your passwords for storage, though, fast is just what you don't want - if you can hash the password in a microsecond, then that means an attacker can try a million passwords every second if they get their hands on your password database.

But you want to slow an attacker down as much as possible, don't you? Wouldn't it be better to use an algorithm which takes a tenth of a second to hash the password instead? A tenth of a second is still fast enough that users won't generally notice, but an attacker who has a copy of your database will only be able to make 10 attempts per second - it will take them 100,000 times longer to find a working set of login credentials. Every hour that it would take them at a microsecond per attempt becomes 11 years at a tenth of a second per attempt.

So, how do you accomplish this? Some folks fake it by running several rounds of MD5/SHA digesting, but the bcrypt algorithm is designed specifically to address this issue. I don't fully understand the math behind it, but I'm told that it's based on the creation of Blowfish frames, which is inherently slow (unlike MD5 operations which can be heavily streamlined on properly-configured hardware), and it has a tunable "cost" parameter so that, as Moore's Law advances, all you need to do is adjust that "cost" to keep your password hashing just as slow in ten years as it is today.

Dave Sherohman
+1. Interesting.
Dean
+3  A: 

Don't use SHA1 or SHA256, as most other people are suggesting. Definitely don't use MD5.

SHA1/256 and MD5 are both designed to create checksums of files and strings (and other datatypes, if necessary). Because of this, they're designed to be as fast as possible, so that the checksum is quick to generate.

This fast speed makes it much easier to bruteforce passwords, as a well-written program easily can generate thousands of hashes every second.

Instead, use a slow algorithm that is specifically designed for passwords. They're designed to take a little bit longer to generate, with the upside being that bruteforce attacks become much harder. Because of this, the passwords will be much more secure.

You won't experience any significant performance disadvantages if you're only looking at encrypting individual passwords one at a time, which is the normal implementation of storing and checking passwords. It's only in bulk where the real difference is.

I personally like bcrypt. There should be a Perl version of it available, as a quick Google search yielded several possible matches.

vonconrad
You have no idea what you are talking about, stay away from security tags. Message digest algorithms are meant digest data very quickly and try to be impossible to reverse. NIST would not approve a slow cryptographic algorithm.
Rook
I'm not talking reversal, I'm talking bruteforce. And if I have no idea what I'm talking about, I assume people like Paul Buchheit(1) (the creator of Gmail) and Phusion(2) have no idea either? Even Google recommends(3) bcrypt in one of its projects. And these were just a few of the links I found simply by Googling "use bcrypt."
vonconrad
Links for previous comment:1: http://paulbuchheit.blogspot.com/2007/09/quick-read-this-if-you-ever-store.html2: http://blog.phusion.nl/2009/08/13/securely-store-passwords-with-bcrypt-ruby-now-compatible-with-jruby-and-ruby-1-9/3: http://code.google.com/p/google-web-toolkit-incubator/wiki/LoginSecurityFAQ
vonconrad
This is just nonsense. There are two types of hashes, cryptographic hashes and non crypto hashes. SHA and MD5 are both crypto hashes. Recall the SSL/TLS protocol, the whole scheme is based on the the fact that you cannot find collisions for these hashes. So if you think that is weak, you better stop ussing https. And that is why MD5 is broken, because with 300 playstations you can find a collision. The only known attack on sha and md5 are rainbow tables, which apply to bcrypt as well. So a very big downvote, because of the nonsense. Because "google said so" is a bad argumentation
Henri
Henri, you're missing my point as well. Just as I'm not talking about reversal, I'm not talking collision either. You can find out any password no matter what encryption algorithm you're using with enough time and CPU, and here is where bcrypt is much better than sha1 or md5, because _it takes longer to generate_. In a simple script I just created where I encrypt "my password" 1000 times with bcrypt and 1000 times with sha1, it takes 96.3 seconds for bcrypt and <0.01 seconds with sha1. Good luck populating those rainbow tables with an algorithm that's 10,000 times slower.
vonconrad
Additionally, I find it interesting that my answer still has four upvotes in addition to yours and unknown's downvotes. And Dave Sherohman, who make the exact same argument as me (albeit somewhat more diplomatic) has (at least) seven upvotes. Maybe there's some merit in what I/we are saying, after all?
vonconrad