Impossible tri-bar

Digital Phenomena - Your first stop for internet consultancy 
Encryption Tutorial — Lesson 2

Page 2 — Using the crypt() Function

Like other hash functions, the crypt() function is a one-way street: There's no decryption. You can use the crypt() function, however, to determine if plain text input matches an encrypted string, by first using crypt() on the plain text. (Bear with me, it'll make sense soon.)

The crypt() function is built into PHP, so no additional libraries are necessary. On Windows systems, however, be sure you have installed the php*_crypt.dll and modified your php*.ini file to recognize the extension.

The crypt() function uses standard DES encryption to encrypt a given string. Passwords on UNIX systems, and passwords created by some Web servers' htpasswd password creation function, use the same DES encryption. Two arguments are used with the crypt() function: the string to encrypt and the "salt" on which to base the encryption.

A what? A "salt" is a little bit of data used to skew an otherwise standard DES encryption algorithm. The salt of a DES-encrypted string is available to you as the first two characters of the final output. This is important to remember — keep on reading to find out why.

If you have used .htaccess-based authentication in the past, you know that usernames and passwords are kept in a file, usually called .htpasswd. The passwords are stored in a format something like this:

joe:WvzodahMR9USk
jane:g3RYjX5evEvdM
julie:YzASzTGEo2VMA

Now, "WvzodahMR9USk" is not Joe User's password. His password is actually "abba001," but after encryption, it looks like "WvzodahMR9USk." Which is the salt ("Wv") tacked on to the DES-encrypted version of "abba001" ("zodahMR9Usk"). Similarly, "g3" is the salt used to encrypt Jane User's password, and "Yz" is the salt used to encrypt Julie User's password. By knowing the salt, you can perform password-matching as outlined below.

You can use an existing .htpasswd file with your PHP-based authentication scripts by using a combination of the substr() and the crypt() function to match the value entered by the user for $PHP_AUTH_PW, and an entry in the .htpasswd file.

For example, let's modify a sample script from Authenticate and Track Users with PHP to use an exisiting .htpasswd file.

Starting at the beginning, use isset() to check to see if a value exists for $PHP_AUTH_USER, and display the username/password dialog box if no value exists.

if (!isset($PHP_AUTH_USER)) {

         header('WWW-Authenticate: Basic realm="Private"');
         header('HTTP/1.0 401 Unauthorized');
         echo 'Authorization Required.';
         exit;

}

If a value does exists for $PHP_AUTH_USER, open the .htpasswd file and read the contents into a variable called $file_contents. Then, separate this string into something useful, by placing each username/password pair into its own array element ($line). Loop through the $line array, further separating these strings into username as one array element and the password as another.

else if (isset($PHP_AUTH_USER)) {

         $filename = "/path/to/.htpasswd";
         $fp = fopen($filename, "r");
         $file_contents = fread($fp, filesize($filename));
         fclose($fp);

         // Place each line in user info file into an array

         $line = explode("\n", $file_contents);

         // For as long as $i is <= the size of the $line array,
         // explode each array element into a username and password
         // pair and attempt to match to $PHP_AUTH_USER and
         // $PHP_AUTH_PW values

         $i = 0;

         while($i <= sizeof($line)) {
                 $data_pair = explode(":", $line[$i]);

Now, take the value of $PHP_AUTH_USER and try to find a match to $data_pair[0] (the username from .htpasswd). If you find a match, then use substr() to strip away the first two characters (the salt) of the corresponding password entry ($data_pair[1]). Next, use the crypt() function to encrypt $PHP_AUTH_PW, using the salt found with substr().

                 if ($data_pair[0] == "$PHP_AUTH_USER") {

                         // get salt from $data_pair[1]
                         $salt = substr($data_pair[1], 0, 2);

                         // encrypt $PHP_AUTH_PW based on $salt
                         $enc_pw = crypt($PHP_AUTH_PW, $salt);

Take the value of $enc_pw and see if it matches the password entry in the .htpasswd file ($data_pair[1]). If a match is found, assign a value of "1" to a variable called $auth, then break out of the loop. Or, if no match is found, assign a value of "0" to $auth and continue the loop.

                         // try to match encrypted passwords
                         if ($data_pair[1] == "$enc_pw") {

                                 $auth = 1;
                                 break;

                         } else {

                                 $auth = 0;

                         }


                 } else {

                         $auth = 0;

                 }

                 $i++;

         }

After the while loop has ended, use the value of $auth to either display the dialog box again or display the secret content (if user is authorized).

         // check value of $auth

         if ($auth == "1") {

                 echo "You're authorized!</p>";

         } else {

                 header('WWW-Authenticate: Basic realm="Private"');
                 header('HTTP/1.0 401 Unauthorized');
                 echo 'Authorization Required.';
                 exit;

         }
}

From top to bottom, the script looks something like this:

<?

if (!isset($PHP_AUTH_USER)) {

         header('WWW-Authenticate: Basic realm="Private"');
         header('HTTP/1.0 401 Unauthorized');
         echo 'Authorization Required.';
         exit;

} else if (isset($PHP_AUTH_USER)) {

         $filename = "/path/to/.htpasswd";
         $fp = fopen($filename, "r");
         $file_contents = fread($fp, filesize($filename));
         fclose($fp);

         // Place each line in user info file into an array

         $line = explode("\n", $file_contents);

         // For as long as $i is <= the size of the $line array,
         // explode each array element into a username and password
         // pair and attempt to match to $PHP_AUTH_USER and
         // $PHP_AUTH_PW values

         $i = 0;

         while($i <= sizeof($line)) {
                 $data_pair = explode(":", $line[$i]);

                 if ($data_pair[0] == "$PHP_AUTH_USER") {

                 // get salt from $data_pair[1]
                 $salt = substr($data_pair[1], 0, 2);

                 // encrypt $PHP_AUTH_PW based on $salt
                 $enc_pw = crypt($PHP_AUTH_PW, $salt);

                 // try to match encrypted passwords
                         if ($data_pair[1] == "$enc_pw") {

                                 $auth = 1;
                                 break;

                         } else {

                                 $auth = 0;

                         }

                 } else {

                         $auth = 0;

                 }

                 $i++;

         }


         // check value of $auth

         if ($auth == "1") {

                 echo "You're authorized!

"; } else { header('WWW-Authenticate: Basic realm="Private"'); header('HTTP/1.0 401 Unauthorized'); echo 'Authorization Required.'; exit; } } ?>

Maybe I'm easy to please, but I think this little modification to the authentication script is pretty cool, especially if you have several years' worth of legacy .htpasswd files sitting on your Web server, yet you want to switch to PHP-based authentication.

In the next section, we get crazy with more add-on encryption libraries.

next page»


|Home|About Us|Services|Search|
|Software|Products|Support|Links|Latest|
W3C validatedW3C validated CSSCompatible with all browsers