ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


PHP's Encryption Functionality

by W.J. Gilmore
07/26/2001

"2db76afcc5c0176b2770fc2360cc1cd4"!

What?!?

I said, "648a19754f7803769c66f871b9cd171a"!

Of course, I don't expect you to be able to understand the above two phrases. In fact, I'm counting on it, because I've encrypted the data to hide the true meaning of the messages. This notion of data encryption plays an increasingly important part of our lives, particularly considering the mammoth amount of transactions and activities that take place online. For those of you responsible for implementing these data security features, you may be interested to know that PHP provides an interesting array of security-oriented functionality. In this article, I'll introduce you to this functionality, providing you with a basis from which you can begin incorporating security enhancements into your own applications.

Preliminary information

Before delving into PHP's security functionality, I'd like to take a moment to introduce you to several notions of cryptography that will be particularly informative to those new to the subject. If you're already familiar with the very basic concepts of cryptography, feel free to skip ahead to the next section.

Cryptography can be generally defined as the study and practice of encryption and decryption, where encryption is the process of converting data into a format unreadable by all except certain parties, and decryption being the process of converting the encrypted data back into its original readable format. This unreadable data is also known as "ciphertext," while the readable data is known as plain text.

Data is encrypted/decrypted using some form of algorithm. These algorithms can be relatively simple, such as the famed Caesar Cipher (supposedly invented by Julius Caesar himself), which involves the shifting of alphabetical characters n places so as to seemingly "scramble" the meaning of the data. Of course, today's algorithms are considerably more complex, and are even considered unbreakable using today's known methods. To put it into perspective, the Caesar Cipher can be broken with patience and a pencil and paper, while it is currently technologically impossible to break even a single key implemented via the advanced encryption standard algorithm Rijndael.

PHP's cryptography functionality

Those of you with even minimal experience with non-Windows platforms are probably familiar with the crypt() function. This function implements what is termed as one-way encryption, which allows for the encryption of some plain text, but does not provide a way in which to convert the ciphertext back to its original form. While on the surface this may seem like a relatively useless idea, it is actually a widely used technique for ensuring the integrity of system passwords. After all, if the one-way encrypted passwords somehow fall into the hands of a third-party, it isn't going to do much good because they can never be converted back to plain text. When it comes time to verify a password input by a user, that input is also encrypted using the one-way algorithm, and compared with the stored encrypted password. If they match, the input password must be correct.

PHP also offers the possibility to perform one-way encryption using its own crypt() function. I'll briefly introduce this function here:

string crypt (string input_string [, string salt])

The input parameter input_string is just the string that you would like to encrypt. The second, optional input parameter salt refers to a bit-string that will influence the encryption outcome to further eliminate the possibility of what are known as precomputation attacks. By default, PHP uses a two-character DES salt string. However, if the encryption standard on your system happens to be MD5 (I'll introduce the MD5 algorithm later), a 12-character salt string is used. Incidentally, you can find out the size of the salt string your system will use by simply executing the following:

print "My system salt size is: ". CRYPT_SALT_LENGTH;

Chances are your system supports additional encryption algorithms. In all, crypt() supports four, each of which is shown below along with its corresponding salt:

Algorithm Salt
CRYPT_STD_DES 2-character (Default)
CRYPT_EXT_DES 9-character
CRYPT_MD5 12-character beginning with $1$
CRYPT_BLOWFISH 16-character beginning with $2$

User authentication with crypt()

As an example of the use of the crypt() function, consider a scenario where you are interested in creating a PHP script that restricts a certain directory, allowing only those users supplying a correct user name and password to enter this directory. I'll store this information in a table residing on my favorite database server, MySQL. I'll begin by creating the table (titled "members"):

mysql>CREATE TABLE members (
    ->username CHAR(14) NOT NULL,
    ->password CHAR(32) NOT NULL,
    ->PRIMARY KEY(username)
    ->);

Next, assume that the following data is found in the members table:

Username Password
clark keloD1C377lKE
bruce ba1T7vnz9AWgk
peter paLUvRWsRLZ4U

These encrypted passwords correspond to "kent", "banner", and "parker", respectively. Notice that the first two letters of each password correspond to their unencrypted counterparts. This is because I used the following code to create a salt based on the first two letters of the password:

// Password is entered in an administration 
// form and stored as 
$enteredPassword.
$salt = substr($enteredPassword, 0, 2);
$userPswd = crypt($enteredPassword, $salt);
// $userPswd is then stored in the MySQL 
// database along with the username.

I'll make use of Apache's challenge-response authentication scheme to prompt the user for a user name and password. A little-known fact about PHP is that it recognizes Apache's challenge-response input user name and password as the global variables $PHP_AUTH_USER and $PHP_AUTH_PW respectively, which I'll make use of in the authentication script. Take a moment to read through the following script, paying particular attention to the comments so as to better understand the code flow:


Listing 1: Using crypt() and Apache's challenge-response authentication scheme.

<?php

$host = "localhost";
$user = "zorro";
$pswd = "hellodolly";
$db = "users";

// Set authorization to False

$authorization = 0;

// Verify that user has entered username and password

if (isset($PHP_AUTH_USER) && isset($PHP_AUTH_PW)) :

  mysql_pconnect($host, $user, $pswd) or die("Can't connect to MySQL
server!");

  mysql_select_db($db) or die("Can't select database!");

  // Perform the encryption
  $salt = substr($PHP_AUTH_PW, 0, 2);
  $encrypted_pswd = crypt($PHP_AUTH_PW, $salt);

  // Build the query

  $query = "SELECT username FROM members WHERE
      username = '$PHP_AUTH_USER' AND
      password = '$encrypted_pswd'";

  // Execute the query

  if (mysql_numrows(mysql_query($query)) == 1) :
    $authorization = 1;
  endif;

endif;

// confirm authorization

if (! $authorization) :

  header('WWW-Authenticate: Basic realm="Private"');
  header('HTTP/1.0 401 Unauthorized');
  print "You are unauthorized to enter this area.";
  exit;

else :

  print "This is the secret data!";

endif;

?>


There you have it -- a simple authentication scheme for verifying user access. Before using crypt() to protect important secrets such as directions to the secret hiding place of the family jewels, keep in mind that crypt() when used in its default form is certainly not the most secure algorithm in the world, and should not be used for anything other than low-level authentication. For those of you searching for a more robust encryption scheme, hold tight, as I'll be introducing several later in this article.

Next, I'll introduce another PHP-supported function -- md5(). This function, which uses the MD5 hashing algorithm, has several interesting uses worth noting.

Hashing

A hash function will transform some variable-length message into a fixed-length hashed outcome, also known as a "message digest." This is useful because this fixed-length string can then be used as a method for checking file integrity and verifying digital signatures, in addition to things such as user authentication. As it pertains to PHP, PHP's built-in md5() hashing function will convert any variable-length message into a 128-bit (32-character) message digest. The interesting thing about hashing is that it is impossible to decode a message by examining the hash, because the hashed result is in no way related to the content of the original plain text. To illustrate this, consider that just changing one character of a string will cause the MD5 hashing algorithm to calculate two vastly different outcomes. First consider Listing 2 and its corresponding outcome.


Listing 2: A string hashed with md5().

<?php
$msg = "This is some message that I just wrote";
$enc_msg = md5($msg);
print "hash: $enc_msg 

"; ?>

The outcome:

hash: 81ea092649ca32b5ba375e81d8f4972c

Notice that the outcome is 32 characters long. Now consider Listing 3, which contains a slightly modified $msg value:


Listing 3: A slightly modified string hashed with md5().

<?php
// Notice that 'message' is missing an 's'
$msg = "This is some mesage that I just wrote";
$enc_msg = md5($msg);
print "hash2: $enc_msg <br /><br />";
?>

The outcome:

hash2: e86cf511bd5490d46d5cd61738c82c0c

As you can see, a minor change in the message string will cause two vastly different hashing outcomes, although each result is still 32 characters. And thus hashing and the md5() function are great tools for checking for even the most minor differences in data.

While crypt() and md5() each have their uses, both are rather limited in terms of functionality. In the next section, I'll introduce two very useful PHP extensions, namely Mcrypt and Mhash, which greatly extend a PHP user's encryption options.

While in the last section you learned just how useful one-way encryption could be, there are times when you will want the option to be able to both encrypt and subsequently decrypt data. Thankfully, PHP offers this possibility in the form of the Mcrypt library extension.

Mcrypt

Mcrypt version 2.4.7 is a powerful encryption library containing 22 block algorithms. Specifically, the following algorithms are supported:

Blowfish RC2 Safer-sk64 xtea
Cast-256 RC4 Safer-sk128
DES RC4-iv Serpent
Enigma Rijndael-128 Threeway
Gost Rijndael-192 TripleDES
LOKI97 Rijndael-256 Twofish
Panama Saferplus Wake

Installation

Mcrypt isn't included in the standard PHP distribution, so you'll need to download it. You can get the latest distribution from ftp://argeas.cs-net.gr/pub/unix/mcrypt/. After you've successfully downloaded the most recent distribution, follow these steps to compile Mcrypt and build the extension into your PHP distribution:

  1. Download the Mcrypt library
  2. gunzip mcrypt-x.x.x.tar.gz
  3. tar -xvf mcrypt-x.x.x.tar
  4. ./configure --disable-posix-threads
  5. make
  6. make install
  7. cd to your PHP directory.
  8. ./configure -with-mcrypt=[dir] [--other-configuration-directives]
  9. make
  10. make install

Of course, depending on your own requirements and the way PHP is installed in relation to your web server, you may need to modify this configuration process.

Working with Mcrypt

Mcrypt is particularly useful not only for the number of encryption algorithms it offers the user, but also because it can be used to encrypt and decrypt data. Furthermore, PHP's Mcrypt extension offers 35 rather useful functions manipulating data. Although a complete discussion of these functions is out of the scope of this article, I'll introduce several of the more prominent ones in this section.

To begin, I'll introduce how data can be encrypted and then later decrypted using the Mcrypt extension. Listing 4 demonstrates this, first encrypting a string, then displaying the encrypted data to the browser, and then decrypting that string and again displaying it in its original format.


Listing 4: Encrypting and decrypting data with Mcrypt.

<?php

// Designate string to be encrypted
$string = "Applied Cryptography, by Bruce Schneier, is 
a wonderful cryptography reference.";

// Encryption/decryption key
$key = "Four score and twenty years ago";

// Encryption Algorithm
$cipher_alg = MCRYPT_RIJNDAEL_128;

// Create the initialization vector for added security.
$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher_alg, 
MCRYPT_MODE_ECB), MCRYPT_RAND);

// Output original string
print "Original string: $string <p>";

// Encrypt $string
$encrypted_string = mcrypt_encrypt($cipher_alg, $key, 
$string, MCRYPT_MODE_CBC, $iv);

// Convert to hexadecimal and output to browser
print "Encrypted string: ".bin2hex($encrypted_string)."<p>";

$decrypted_string = mcrypt_decrypt($cipher_alg, $key, 
$encrypted_string, MCRYPT_MODE_CBC, $iv);

print "Decrypted string: $decrypted_string";

?>

Executing Listing 4 will produce the following output:

Original string: Applied Cryptography, by Bruce Schneier, is a wonderful cryptography reference.

Encrypted string: 02a7c58b1ebd22a9523468694b091e60411cc4dea8652bb8072 34fa06bbfb20e71ecf525f29df58e28f3d9bf541f7ebcecf62b c89fde4d8e7ba1e6cc9ea24850478c11742f5cfa1d23fe22fe8 bfbab5e

Decrypted string: Applied Cryptography, by Bruce Schneier, is a wonderful cryptography reference.

It's likely that the two most prominent functions in Listing 4 are mcrypt_encrypt() and mcrypt_decrypt(), the utility of each being obvious. I use the mode known as "Electronic Codebook Mode." Mcrypt offers several encryption modes, all worth examining because each has specific characteristics that can influence the security of the cipher. For those of you new to the world of cryptography, you may be curious to learn more about the function mcrypt_create_iv(). While a thorough explanation is out of the scope of this article, I will mention that this function creates an initialization vector (hence, iv), which makes each message unique. While the initialization vector is not used in every mode, PHP will complain if it is not used along with those in which it is required (cbc, cfb, and ofb modes).

Mhash

The Mhash library extension provides support to 12 hashing algorithms (as of version 0.8.3). An examination of the Mhash v.0.8.3 header file (mhash.h) shows that it supports the following hashing algorithms:

CRC32 HAVAL160 MD5
CRC32B HAVAL192 RIPEMD160
GOST HAVAL224 SHA1
HAVAL128 HAVAL256 TIGER

As you can see, there are many hashing algorithms to choose from.

Installation

Like Mcrypt, Mhash is not included in the default PHP distribution. You can download it. For non-Windows users, here is the installation process:

  1. Download the Mhash library
  2. gunzip mhash-x.x.x.tar.gz
  3. tar -xvf mhash-x.x.x.tar
  4. ./configure
  5. make
  6. make install
  7. cd to your PHP directory.
  8. ./configure -with-mhash=[dir] [--other-configuration-directives]
  9. make
  10. make install

Again, depending upon how PHP is installed in relation to your Web server, you may have to perform extra configuration steps.

For Windows users, you may be interested to know that http://www.php4win.de offers a great Win32 PHP distribution packed with the Mhash extension included. Just download and unzip the distribution, and follow the directions found in the readme.first document.

Working with Mhash

Hashing a message is easy. Consider the following example:

<?php
$hash_alg = MHASH_TIGER;
$message = "These are the directions to the 
secret fort. Two steps left, three steps 
right, and cha cha cha.";
$hashed_message = mhash($hash_alg, $message);
print "The hashed message is ". bin2hex($hashed_message);
?>

Executing this script will yield the outcome:

The hashed message is 07a92a4db3a4177f19ec9034ae5400eb60d1a9fbb4ade461

Notice that the function bin2hex() is used to facilitate the output of $hashed_message. This is because the hashed outcome is in binary format, and must be converted to hexadecimal in order to be converted to a readable format.

Remember that because the hash is a one-way function and its output is not dependent upon the input, you could display this message in public view. In fact, this strategy is commonly used to allow users to compare message digests of downloaded files with those provided by the system administrator to ensure that they have not been corrupted or compromised.

Mhash also offers a few other useful functions. For example, suppose I wanted to output the name of a particular Mhash supported hashing algorithm. Based on the premise that Mhash assumes all supported algorithms begin with MHASH_, I could execute the following:

<?php
$hash_alg = MHASH_TIGER;

print "This data has been hashed with the 
".mhash_get_hash_name($hashed_message)."
 hashing algorithm.";
?>

And the resulting output:

This data has been hashed with the TIGER hashing algorithm.

A final note about PHP and encryption

One final very important note to make about PHP and encryption is that any data transmitted between the server and the client (and vice-versa) is not secure while in transit! PHP is a server-side technology, and can do nothing to prevent snoopers from watching this data in transit. Therefore, if you are interested in implementing a complete security application, I would suggest checking out Apache-SSL, or any of the other reputable secure-server implementations.

Conclusion

This article introduced one of PHP's particularly cool functions: data encryption. I discussed not only PHP's built-in crypto-functions (namely crypt() and md5()), but also introduced the two powerful extensions -- Mcrypt and Mhash. In closing, I'd like to point out that a truly secure PHP implementation would most likely involve the use of a secure server. PHP is a server-side language and therefore cannot protect data as it travels from the client to the server.

Resources

If you are interested in learning more about encryption, take some time to check out the following resources:

W.J. Gilmore has been developing PHP applications since 1997, and is frequently published on the subject within some of the Web's most popular development sites. He is the author of 'A Programmer's Introduction to PHP 4.0' (January 2001, Apress), and is the Assistant Editorial Director of Web and Open Source Technologies at Apress.


Return to the PHP DevCenter.

Copyright © 2009 O'Reilly Media, Inc.