Jump to content

crypt() in PHP 5.2.8 vs PHP 5.3.0


PP133

Recommended Posts

Hey guys, first post here, I'm hoping you can help me out.

 

Basically, I have two servers, one runs PHP 5.3.0 and one runs PHP 5.2.8. On each of those servers, the crypt function returns a different encrypted string, when given the same salt and input. This is causing some problems as a tool I'm developing, on the server running 5.3.0, is attempting to authenticate logins through a database whose passwords were created using the crypt function in PHP 5.2.8.

 

Here's the code that I'm running on both servers:

 

<?php

$username = 'aardvark';

$password = 'password';

$salt = substr($username, 0, 1);

$pass = crypt($password, $salt);

echo "\$pass = $pass\n";

?>

 

Here's the output on the 5.3.0 server:

 

$ php test23.php

$pass = a$Av8awQ0AsR6

 

 

And here's the output on the 5.2.8 server:

 

$ php test23.php

$pass = a$LHSkrbhfU1.

 

 

Now, if there's no other way about this, I can downgrade my PHP to 5.2.8 to play nice with the other server, but it seems odd to me that the crypt function would be upgraded, which it appears to have been for 5.3.0 and 5.3.2, but not be made backwards compatible. I guess what I'm asking here is whether there's some argument that I can give crypt so that it'll work like it did in PHP 5.2.8 from within PHP 5.3.0.

 

Thanks in advance.

Link to comment
Share on other sites

you should read the note on

http://php.net/manual/en/function.crypt.php

 

Note: As of PHP 5.3.0, PHP contains its own implementation and will use that if the system lacks of support for one or more of the algorithms.

 

Hey, thanks for the reply.  I did actually read that but that didn't really help.  I understand the crypt function in 5.3.0 is new, but is there really no way to make it backwards compatible?  What about people that built databases using the crypt from 5.2.8, are they forever doomed to stay in that version because crypt is not backwards compatible?  I mean, I'm sure there's a way to convert to another encryption, but you know what I mean.

Link to comment
Share on other sites

That PHP contains its own implementation shouldn't make a difference. The algorithm should be the same.

 

As far as I can tell, crypt() is a hashing function, and the algorithm it chooses depends on what is available on the system. Apparently, you can choose what algorithm to use by prefixing the salt with various things. I don't know.

 

Anyway, any particular reason you can't use another hashing function, like sha1()?

 

 

Link to comment
Share on other sites

That PHP contains its own implementation shouldn't make a difference. The algorithm should be the same.

 

As far as I can tell, crypt() is a hashing function, and the algorithm it chooses depends on what is available on the system. Apparently, you can choose what algorithm to use by prefixing the salt with various things. I don't know.

 

Anyway, any particular reason you can't use another hashing function, like sha1()?

 

Hey, thanks for answering.

 

I have no problems with using another encryption algorithm, but the encrypted string the algorithm returns has to match the encrypted passwords that already exist in the database.  That database, the one I'm trying to authenticate through, has a login table with passwords that were created using the crypt function in PHP 5.2.8.  See what I mean?  I'm authenticating kinda like this:

 

...

$username = $_POST['username'];

$password = $_POST['password'];

$salt = substr($username, 0, 1);

$encrypted_password = crypt($password, $salt);

$query = "select firstname from logins where username = '{$username}' and password = '{$encrypted_password}'";

$result_query = mysql_query($query);

...

Link to comment
Share on other sites

The only method you'll get to achieve compatibility is to batch decrypt the passwords and hash them like they were designed to be in the database. *SQL provides md5() and sha1() commands (without using PHP), as they're standard.

 

If you're wanting to know the exact differences, I've made a textdiff between the two versions here:

http://zwap.to/0023g

Link to comment
Share on other sites

The only method you'll get to achieve compatibility is to batch decrypt the passwords and hash them like they were designed to be in the database. *SQL provides md5() and sha1() commands (without using PHP), as they're standard.

 

If you're wanting to know the exact differences, I've made a textdiff between the two versions here:

http://zwap.to/0023g

 

Hey, thanks for the help.

 

Well, that is one thorough answer, including the library, hehe.  While I'm not a stranger to C (I believe that's what that's written in) I'm having a bit of a tough time following along without more documentation.  However, I do see that certain things were added, such as SHA256 and SHA512.

 

How can I decrypt the passwords?  My understanding has been that crypt creates a one-way encryption, hence the reason I'm trying to get crypt to spit out the same encrypted string in 5.3.0 as in 5.2.8, so that I can match the encrypted version of user input on 5.3.0 to the encrypted password in the database on 5.2.8.

Link to comment
Share on other sites

Upon looking more, it looks like it's changed the way the salt is implemented in the Unix DES library, possibly using another modified algarithm. You can look online (user contrib. notes) for samples of this; I'm sure someone else has run into this problem. But you're most likely in a bad place, as you've generated defective/depricated one way hashes.

Link to comment
Share on other sites

Upon looking more, it looks like it's changed the way the salt is implemented in the Unix DES library, possibly using another modified algarithm. You can look online (user contrib. notes) for samples of this; I'm sure someone else has run into this problem. But you're most likely in a bad place, as you've generated defective/depricated one way hashes.

 

I would think that others would've run into this but I didn't have much luck finding anything, that's why I came here.

 

And don't blame me for this mess, another team decided to go about it this way, not me :)

Link to comment
Share on other sites

"

As of PHP 4.1, if no salt is provided, crypt() will use MD5 encryption and randomly generate a 12-character salt.

Older versions of PHP will use the first two characters of the password string as a salt if none is provided, and it uses DES-based encryption.

A salt of nine or more characters will use extended DES.

Use $1$ to start a 12-character salt to use MD5. Note that the last character will always be $, and extraneous characters will be ignored. An example looks like this: $1$blahblah$.

Use $2$ to start a 16-character salt to use Blowfish.

If anything not matching one of the above requirements is used as a salt or the relevant encryption type is not supported, the first two characters will be taken and DES will be used.

"

http://articles.techrepublic.com.com/5100-10878_11-1058691.html

 

so by using the first 2 characters of the user name to generate the salt, it seems standard des will be used.

 

To verify this use:

if (CRYPT_STD_DES == 1)

{

echo "Standard DES: "

}

 

on your 5.2.x box.

 

Then you are going to have to figure out how to implement standard DES in 5.3  (remember only 8 characters of the password will be relavent).  It actually would be easier to bruteforce all the passwords if you really are using DES.  How many users are there?

Link to comment
Share on other sites

"

As of PHP 4.1, if no salt is provided, crypt() will use MD5 encryption and randomly generate a 12-character salt.

Older versions of PHP will use the first two characters of the password string as a salt if none is provided, and it uses DES-based encryption.

A salt of nine or more characters will use extended DES.

Use $1$ to start a 12-character salt to use MD5. Note that the last character will always be $, and extraneous characters will be ignored. An example looks like this: $1$blahblah$.

Use $2$ to start a 16-character salt to use Blowfish.

If anything not matching one of the above requirements is used as a salt or the relevant encryption type is not supported, the first two characters will be taken and DES will be used.

"

http://articles.techrepublic.com.com/5100-10878_11-1058691.html

 

so by using the first 2 characters of the user name to generate the salt, it seems standard des will be used.

 

To verify this use:

if (CRYPT_STD_DES == 1)  echo "Standard DES: "

on your 5.2.x box.

 

Then you are going to have to figure out how to implement standard DES in 5.3  (remember only 8 characters of the password will be relavent).  It actually would be easier to bruteforce all the passwords if you really are using DES.  How many users are there?

 

 

 

Hey, thanks for chiming in.

 

Well, this is interesting, I'm learning something :)

 

I wrote that is-STD-DES-available code snippet into my code, and it is available on the 5.3.0 and 5.2.8 servers (so are Blowfish and MD5).

 

I have to correct you on one point, I'm using a one character salt, the first character of $username.  Taking that into consideration, I wondered what would happen given the paragraph you quoted:

 

"If anything not matching one of the above requirements is used as a salt or the relevant encryption type is not supported, the first two characters will be taken and DES will be used."

 

I figured that crypt would take the first two characters of the password, but comparing the output of crypt($password, 'a') and crypt($password, 'pa'), I found they don't match.  I did, however, find that crypt seems to add a $ to your salt if the salt is only one character.  Once I learned this, I was able to verify that the crypt function is indeed using standard DES encryption when given a one character salt and that it is adding a $ to the salt.  Take a look at the following:

<?php
        if(CRYPT_STD_DES == 1) { echo "Standard DES is available.\n\n"; }

        $username = 'aardvark';
        $password = 'password';

        $salt = substr($username, 0, 1);
        echo "\$salt = $salt\n";

        $pass = crypt($password, $salt);
        echo "Standard crypt encryption (1 char salt)  = '$pass'\n";

        $des_pass = crypt($password, 'a$');
        echo "DES encryption (2 char salt) = '$des_pass'\n";
?>

 

And it's output (running PHP 5.3.0):

$ php test24.php

Standard DES is available.

 

$salt = a

Standard crypt encryption (1 char salt)  = 'a$Av8awQ0AsR6'

DES encryption (2 char salt) = 'a$Av8awQ0AsR6'

 

And finally the output of the same code on PHP 5.2.8:

$ php test24.php

Standard DES is available.

 

$salt = a

Standard crypt encryption (1 char salt)  = 'a$LHSkrbhfU1.'

DES encryption (2 char salt) = 'a$LHSkrbhfU1.'

 

As you can see from the output, the standard one character salt crypt function gives us the same output as the two character salt (adding a $ to our one character salt) crypt function.

 

I suppose the findings are all good, even though we've yet to solve the problem, at least we can say that crypt is definitely using standard DES to encrypt the password, the question now stands as why the stanard DES encryptions of the same string using the same salt do not match across two servers.

 

I should add that we've compiled 5.2.8 on the server running 5.3.0 and verified that the problem has gone away, i.e. using the PHP 5.2.8 binary on the server running 5.3.0 gives the same encrypted output as the other server running 5.2.8, just so we don't start thinking it's a problem with the servers themselves (libraries, binaries, etc.) and not the PHP version.

Link to comment
Share on other sites

How many users are there?

 

Almost forgot about that question...there are over 2300 users in the database, though only a very small sub-set of those users will use this particular tool that I'm working on.  I could probably trim that number down to 50 or so users that would probably use this tool but that's just a pain and doesn't work going forward (new hires).

Link to comment
Share on other sites

One way you can get around this issue is to generate new passwords for all your users and hash them using new algorithm.

Send them kind emails explaining that this is to improve security of their accounts, and they should be fine with that (at least most of them).

Link to comment
Share on other sites

One way you can get around this issue is to generate new passwords for all your users and hash them using new algorithm.

Send them kind emails explaining that this is to improve security of their accounts, and they should be fine with that (at least most of them).

 

This isn't a favorable option as we're trying to make it easy for users to use this tool, not give them another password to remember (though they could just enter the same password again).  They don't care if their passwords are secure, I'm sure most of them are "1234abcd" :)

Link to comment
Share on other sites

Well, if all you have to do is put a dollar sign in at the end of the salt to make it work with 5.3, then why not do that?  Or is that what you were explaining?  Is this solved. is more what I'm asking.

 

Hey, no actually that doesn't fix it.  With or without that additional $ in the salt, the output of crypt is different between 5.2.8 and 5.3.0:

 

PHP 5.3.0

    $ php test24.php

    Standard DES is available.

 

    $salt = a

    Standard crypt encryption (1 char salt)  = 'a$Av8awQ0AsR6'

    DES encryption (2 char salt) = 'a$Av8awQ0AsR6'

 

 

 

PHP 5.2.8:

    $ php test24.php

    Standard DES is available.

 

    $salt = a

    Standard crypt encryption (1 char salt)  = 'a$LHSkrbhfU1.'

    DES encryption (2 char salt) = 'a$LHSkrbhfU1.'

 

Link to comment
Share on other sites

oh.oops. I overlooked that..

 

Well, in my opinion, your best bet is to go with Mchl's suggestion.  Send every user a link (by email) to a "login page".  Then use an encryption of what they type for their password as the new password.

 

It'd be a little less complicated that digging out the crypt function in 5.2 and merging it with your 5.3 installation.

Link to comment
Share on other sites

If the users don't care about their password security why not siphon them to cleartext and hash them with md5 or sha1 until you get them all?  Continue using the php5.2. until sufficient users (say 90%) have logged in.  The users that are left, just have them fail, roll them to the old server (you're going to have to keep this one online for a while to do this) and siphon to the new database. 

Link to comment
Share on other sites

I may investigate this issue a little more before I give up, but I think that if I do decide to go another route, I might just have the users create the same logins on this tool as they have on the other tool running on 5.2.8.  Shouldn't cause too many problems.

 

I'll update this thread if I find anything over the next few days.

 

Thanks for all of the suggestions guys.

Link to comment
Share on other sites

Interesting development...someone replied to my bug report stating that you can only use 0-9, A-Z, a-z for a salt, so my salt including a $ might do unexpected stuff.

 

Using the salt 'aa' with the same code, I get the same encrypted output

<?php

        $password = 'password';

 

        $salt = 'aa';

        $des_pass = crypt($password, $salt);

        echo "DES encryption (salt = $salt) = $des_pass\n";

?>

 

PHP 5.3.0:

 

$ php test27.php

DES encryption (salt = aa) = aajfMKNH1hTm2

 

PHP 5.2.8:

 

$ php test27.php

DES encryption (salt = aa) = aajfMKNH1hTm2

 

This is good, I suppose, but I'm still having issues with the one-character-salt-created encryption not matching across 5.2.8 and 5.3.0.  Reported it to the same bug place, let's see what they say.

Link to comment
Share on other sites

generate a salt in your 5.2 version for a given password with salt a$

 

In 5.3 try a for loop($i) 0-255 and try a salt with "a"+chr($i) and see if it comes up with a different special character ??  Couldn't hurt.

 

Sorry I've been a bit absent, had to work on some other stuff.

 

This is a neat idea, but I picked up on the pattern with the two character salts way back when I first posted this:  the first two characters of the encrypted output are the salt.  See for your self:

 

PHP 5.2.8

DES encryption (salt = a!) = a!sgBMFkfptE6

DES encryption (salt = a") = a"GMnFHzfE.q6

DES encryption (salt = a#) = a#Gl30xzHaZxE

DES encryption (salt = a$) = a$LHSkrbhfU1.

DES encryption (salt = a%) = a%2jDWNsfCiWM

DES encryption (salt = a&) = a&1ozOtJW9BFA

DES encryption (salt = a') = a'05n2JEUfXYI

DES encryption (salt = a() = a(YCUPgS0kJzQ

DES encryption (salt = a)) = a)DMOnwont4ng

 

PHP 5.3.0

DES encryption (salt = a!) = a!Av8awQ0AsR6

DES encryption (salt = a") = a"Av8awQ0AsR6

DES encryption (salt = a#) = a#Av8awQ0AsR6

DES encryption (salt = a$) = a$Av8awQ0AsR6

DES encryption (salt = a%) = a%Av8awQ0AsR6

DES encryption (salt = a&) = a&Av8awQ0AsR6

DES encryption (salt = a') = a'Av8awQ0AsR6

DES encryption (salt = a() = a(Av8awQ0AsR6

DES encryption (salt = a)) = a)Av8awQ0AsR6

 

So going off of the evidence above, it appears that if you don't include a second character for a salt, crypt adds a $ to your salt.  Look at the examples below, the second character is a $:

 

PHP 5.2.8

$ php test27.php 
DES encryption (salt = a) = a$LHSkrbhfU1.  

 

PHP 5.3.0

$ php test27.php
DES encryption (salt = a) = a$Av8awQ0AsR6

 

And we can verify this by explicitly making the second character in the salt a $:

 

PHP 5.2.8

$ php test29.php 
DES encryption (salt = a) = a$LHSkrbhfU1.
DES encryption (salt = a$) = a$LHSkrbhfU1.

 

PHP 5.3.0

$ php test29.php 
DES encryption (salt = a) = a$Av8awQ0AsR6
DES encryption (salt = a$) = a$Av8awQ0AsR6

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.