Passwords in PHP

Category:WikiProject Cryptography participants
Image via Wikipedia

Generally speaking it is a really bad idea to hold passwords in cleartext. I am actually amazed people still do this!  The standard way of holding passwords that has been around for years is to encrypt or hash the password and store the result, called a ciphertext.  There have been many ways of hashing the password, starting off with plain old crypt with no salt (a random pair of characters) then crypt with salt through to MD5 and SHA.

The thing is, each one of these hashing techniques results in a ciphertext in a different length.  Now with most languages, this doesn’t matter because you know what hash you are using; its simply the name of the function or some flag you set.

PHP is different, because all of these methods use the one function called crypt which is a little confusing because it is more than plain old crypt.  Around the PHP version 5.3 the developers started putting in the more complex hash algorithms which is good, but the ciphertext has been growing.

A lot of applications store this hashed password in a database and the decision needs to be made; how big should this field be?  For a long while, 50 characters would be enough and this is what programs like JFFNMS use.  Unfortunately the SHA-512 algorithm needs 98 characters so what you get is half a hash stored. When the user enters their password, the program compares the full hash from that password to the half hash in the database and it always fails.

I’ve adjusted JFFNMS to use 200 character long fields which is fine for now. The problem is who knows what the future will bring?

Enhanced by Zemanta

Comments

16 responses to “Passwords in PHP”

  1. You should be using bcrypt, not SHA-n, however big you make ‘n’.

    SHA and MD5 are general purpose functions, designed to calculate a digest in as short a time as possible. However, being fast is not a desirable property for a digest function for storing passwords: a modern computer can calculate the SHA1 hash of hundreds of megabytes every second. If your users have passwords which are lowercase, alphanumeric, and 6 characters long, you can try every single possible password of that size in less than a minute.

    bcrypt solves this problem by being extremely slow – with a “work factor” of 12, bcrypt is approximately 4 orders of magnitude slower than SHA1. Instead of a single minute to a crack a short password of the kind described above, would take an entire day. Futhermore, this work factor is configurable so it can be increased as computers get faster.

    1. I don’t think bcrypt is available on PHP which is unfortunate. I can see good reasons why, but it looks strange saying “this process is so slow” and it is actually a good thing.

      Unfortunately, I think we’re stuck with SHA or MD5 (or worse) on PHP for a little while.

      1. Actually, there is (the unfortunately named) phpass http://www.openwall.com/phpass/

    2. Anonymous Avatar
      Anonymous

      One major problem with bcrypt: if you use something like http authentication (fairly sensible if done over https, since it proves easier to do from scripts than cookie-based sessions), you end up re-validating the password on every request. If each such validation takes a significant fraction of a second…

  2. toupeira Avatar
    toupeira

    Why not simply use sha1() and a 40-character field?

    I never used crypt() so far, and after reading through its documentation I’m glad I did because it actually forces you to pass arguments inside the salt string. Oh those silly PHP guys…

    1. You basically feed the known hash back into the function as the hash holds the salt and also encodes the type of hash being used. The problem with forcing the hash to SHA1 is that the databases may (and most likely will) still hold old hashed passwords in who knows what format.

      Perhaps it is best to use the crypt() for checking the password and sha1() for encoding it. It’s not ideal but better than storing it in cleartext.

  3. Well I never trust my users. Whenever I have to save passwords I hash them before saving them on the database, but before hashing them, I change them.
    Let’s say user uses “myDog” as password. I could do this:

    $now = time();
    $password = "myDog";//supplied by user
    $newPwd = $now . $password;
    $pwd2save = sha1($newPwd);

    Then I would save the hashed password, and the date of signup. No salt is apparently saved on the database, and I can easily recreate the real password using the user supplied password and the information saved on the database. If someone manages to get the hashed version of the passwords, and decrypts them, it would still not help him much since the real password is still unknown.
    For a real life example I would use a more robust password “mutating” mechanism, but the idea holds the same.
    The only way someone could get the real password would be by getting the source code, but then, if the attacker has access to the files on the server, and the database, I’m pretty much screwed up anyway.

    1. I wondered about the “trusting your users”. I think you mean you don’t trust them to create passwords good enough to not be cracked, given the hash.
      So it’s more that you know they will use pretty useless or weak passwords so you store them with a bit more entropy.
      You’re right about access to the files on the server, except for one reason. If they have that it is pretty much game over. The exception being when your php (or whatever language it is) interpreter fails and the webserver sends the raw script and not the interpreted output.

  4. k3ninho Avatar
    k3ninho

    For knowing the length of your hash field, may I suggest a different data structure? Run-length encoding is a good friend: using two fields, it gives you a bit of space for metadata about how long the data field is, and then stores the data after that. Does PHP allow you to do this?

    K3n.

    1. In PHP it is probably a little too easy to do things like that as it is not a strongly typed language. Field length is almost never a problem within that language but of course you have all the other problems you get with no data structure. If you want your string longer, just do $blah .= "extra stuff"; and it is done.
      The problem is in the database storage which is where this post started from. You could use a blob, but that just seems excessive for storing a hashed password.

  5. Just a little comment as to why people still store passwords in plain text: sometimes – just sometimes – it is necessary for implementing challenge/response authentication. But for applications that don’t need it, it really does not make much sense.

    However, sometimes it is not really easy during the initial design phase to guarantee that nobody will ever ask for challenge/response auth sometime in the future 🙂

  6. Hartmut Holzgraefe Avatar
    Hartmut Holzgraefe

    The real problem seems to be that it is possible to call PHP crypt() without $salt.

    If you pass in an explicit salt, as it is mandatory with libc crypt, you will get results of a length defined by the format of the salt string use.

    When not passing in a salt string yourself you tell PHP to generate one itself, piking the salt format / hash algorithm, and so indirectly picking the result lenth too, that it deems to be the optimal one of those algorithm supported by the actual PHP binary.

    So please don’t blame PHP for the misuse of one of its features …

    1. The libc crypt() is basically the same as the PHP one. Well the Linux glibc one let’s say. The original one only did DES but you can also do blowfish, md5 or SHA in glibc. You’re absolutely right about if you create your salt yourself (which is mandatory in for the libc version) you determine the algorithm.
      And that, it seems, is how you fix your length of your password hash to a known length!

    2. You have shed a ray of sunhnise into the forum. Thanks!

  7. Wonderful post.Nice to read about about md5() or sha1()

Leave a Reply to Peter Pentchev Cancel reply

Your email address will not be published. Required fields are marked *