My application can authenticate via openid and oauth (facebook, twitter, etc) and also with its own authentication system. I previously switched hashing from MD5 to SHA1 and during migration I had to customize so that accounts could verify both with MD5 and SHA1 and now I've removed the MD5 completely.

I read here somewhere that for passwords in fact SHA1 should not be used and bcrypt or something else instead since SHA1 is not specifically a password algorithm and instead is a hash function implementation while a password algorithm would have other more specific features. Can you tell me more about this? Should I switch from SHA1, is it important or premature / paranoia since I never even had a production security issue that I know? I can think of some possible actions:

1. Staying with the current implementation (SHA1 and salt) since the main function of my service isn't user accounts anyway
2. Passing the algorithm as a parameter to the encryption and thus being able to authenticate with different and new algorithm i.e. now using SHA1 and later its easy to switch
4. Does the issue of "real" and "fake" salt mean a lot in practice or just for highest security? Am I using real or fake salt in detail, when the following is the implementation:

def __encrypt(self, plaintext, salt=""):
"""returns the SHA1 hexdigest of a plaintext and salt"""
phrase = hashlib.sha1()
phrase.update("%s--%s" % (plaintext, salt))
return phrase.hexdigest()
import datetime
if not self.salt:
self.salt = self.__encrypt(str(datetime.datetime.now()))


Please tell me if you can comment.

Thank you

-

If your password database will never be compromised, you can store plaintext passwords and nobody will be bothered. The only thing about plaintext passwords is that they can be accidentally remembered by admins who see them - a simple base64 will fix that. Equivalently, MD5 or SHA1, with salt or without, is just as fine.

If your password database is possibly compromised at some point in the future, a single SHA-1 with salt still means that 99% of the user passwords are crackable on a laptop - basically no protection at all, except inconvenience. So if this is the attack you are worried about, you need to switch to something which has a sequential iteration count that isn't trivially small. PBKDF2, bcrypt and scrypt are all good choices for this.

-
Thank you for the insights. Both code and password database could be compromised with email addresses and a user may use the same password for different services so I want to protect from brute force attacks. Now I understand what the weakness with SHA1 can be compared to crypt. I look at how django implemented passwords with python and django supports SHA1, old MD5 for backwards-compatibility and also crypt (from code.djangoproject.com/browser/django/trunk/django) –  Niklas in Stockholm Sep 5 '11 at 11:56
Actually, using crypt(3) doesn't actually fix the algorithm to anything specific - there are many different implementations of the UNIX crypt and some of them provide really weak security. Please see more at: en.wikipedia.org/wiki/Crypt_(Unix) and manpages.courier-mta.org/htmlman3/crypt.3.html Also, for example the SHA-256 and SHA-512 algorithms for crypt(3) are really weird: akkadia.org/drepper/SHA-crypt.txt I'm not sure if I can recommend those fully. –  Nakedible Sep 5 '11 at 18:57
If it wasn't obvious, import crypt in Python is just a wrapper for the crypt(3) function in the underlying platform - and as such, behaves differently on different UNIX systems. –  Nakedible Sep 5 '11 at 18:59
The very very very old DES crypt was vulnerable to just about everything and ignores everything after the first 8 characters. The crypt(3) in modern Linuxes uses SHA-256, has no password length limits, has a configurable amount of rounds just like PBKDF2, uses a 16 character salt, etc. The same function on OpenBSD is bcrypt. crypt(3) is just an interface. –  Nakedible Sep 5 '11 at 20:11
@Nakedible: Which would IMHO be a good argument not to use crypt - it may not provide adequate security on some systems, and there's no easy way to tell. –  Piskvor Sep 8 '11 at 13:47

The distinction between real and fake salt is arbitrary. I suppose that your method would be called "fake salt" by those who make the distinction (concatenation versus "separate input parameter to the algorithm), but there is not really a difference in practice, as long as the hash algorithm is secure.

A bit more secure (in the sence of "provenly secure") would be using HMAC(password, salt), i.e. HASH(key2 || HASH(key1 || salt)), where key1 and key2 are somehow derived from your password (see RFC 2104 for details).

But the real problem with using any fast hash function (like SHA-1) for password hashing is that it allows a brute-force attack by iterating only the password space (instead of a large part of the whole input space, i.e. on average $2^{\text{output size} - 1}$ tries to find a preimage). As SHA-1 is really fast (you can do millions of hashes in a second), and also easily massively parallelizable on GPUs, anyone who gets a copy of your password database can crack most of the passwords in a relatively short time.

Bcrypt avoids this by making the hashing slow. Based on Blowfish's key derivation function, it uses a configurable work factor, so you can make the login just that slow that your users still accept it, at the same time making it really hard for attackers to brute-force many such passwords. Bcrypt also uses some amount of frequently accessed and changed memory (4 KB), which makes it less easily parallelizable on GPUs (which usually do not have enough such space quickly accessible for each core).

A later development is scrypt, which makes the parallelization even more difficult, by also using a (configurable) large amount of RAM (which is nowadays the most costly resource for brute-force attackers, but available on normal PCs).

1. If you can make sure that your user database can't fall in wrong hands, stay with your SHA-1. Since you in reality can't be sure, don't. (For example, often old backups are much less secured than the life data.) Even if the access to your service is not that important, users often reuse passwords for different services, and you don't want to be the one which will be blamed if one of your users' online-banking account gets hacked.
2. Making the algorithm configurable seems like a good idea. You could have a look at how crypt stores the scheme identifier (and parameters, for some schemes) together with the salt and hash, as an inspiration.
3. Becoming an OAuth provider yourself does not really solve the problem, it only shifts it to the part of the program which finally implements the provider. It might have its own advantages (having only one way of handling this instead of two), but these are not of cryptographic nature.
4. As said before, the distinction between real and fake salt is not the important one.
-
Thank you Paulo for a great answer. Today I looked at how django has implemented a solution and they are making algorithm configurable: if algorithm == 'crypt': ... elif algorithm == 'sha1': in the method django.contrib.auth.utils.get_hexdigest so I can look how django allows different algorithms to be used (crypt, sha1, md5) and therefore I may do an import crypt similarly to django (code.djangoproject.com/browser/django/trunk/django). –  Niklas in Stockholm Sep 5 '11 at 11:46
Actually bcrypt was designed by Provos ans Mazières, while scrypt is from Percival. Not the same author. I do not know where the confusion comes from, although I know that I used to be guilty of it, too. –  Thomas Pornin Sep 5 '11 at 19:24
@Thomas: Looks like I misremembered or misunderstood the last paragraph in your answer to "Why can't one implement bcrypt in Cuda?", and didn't care to look it up. Thanks for the correction. –  Paŭlo Ebermann Sep 5 '11 at 21:24

Regarding your question about choice of password hashing algorithms, this has been covered well on this site. See the following posts for excellent advice:

In short, yes, you should use bcrypt, PBKDF2, or scrypt to hash your passwords.

(I differ a bit from other questioners who said SHA1 is fine if your database will never be compromised. History has taught us that database compromise is a real threat, and not one that you can be confident won't happen to you, so you should probably include fallback defenses against it. Since it is relatively easy to use bcrypt/PBKDF2/scrypt instead of SHA1, I suggest you do so.)

-
I agree. It worked being comptaible with SHA1 and MD5 at the same time so I suppose I can stay compatible with many algorithms and definitely use those that are specifically recommended as password hashing algorithms (not just a hashing algorithm but spec designed for passwords) –  Niklas in Stockholm Sep 9 '11 at 23:57