Strong cryptography in PHP


by Enrico Zimuel

UPDATE: I updated this article with some information about password cracking with the reason to use the algorithms bcrypt or scrypt to store the user’s password instead of hash + salt mechanisms. Moreover, I proposed a new RNG based on the usage of /dev/urandom if OpenSSL is not available.

If you are a professional web developer, security is an important aspect of your job. If you are planning to store some critical or sensitive data in your web application, like passwords, credit cards, etc, you should use strong cryptography to protect these data.

What is strong cryptography?

Strong cryptography is the usage of systems or components that are considered highly resistant to cryptanalysis, the study of methods to cracking the codes.
Theoretically speaking, if we encrypt and store sensitive data in a database, file, or whatever, a malicious attacker will not be able to decrypt it without the knowledge of the key, a sequence of numbers used to encrypt or decrypt data. How can we proof that an attacker will not be able to decrypt the data? Unfortunately, the correct answer is that we cannot be sure. We can obtain only a good level of security using well tested algorithms of cryptography (strong cryptography).

For instance, the ENIGMA cipher, used in the second world war as system to encrypt the communication between german soldiers, today is not considered cryptographically strong.
The DES, a FIST standard algorithm in 1976 that, unfortunately, is still used in many systems, is not considered strong cryptography anymore. In the 1998 the Electronic Frontier Foundation (EFF) built a machine, the EFF DES cracker, to perform a brute force search of DES cipher’s key space — that is, to decrypt an encrypted message by trying every possible key. The aim in doing this was to prove that DES’s key is not long enough to be secure. This machine was able to find the key of an encrypted message in less than 1 day and we are talking about 1998!

Currently, some of the algorithms that can be considered cryptographically strong are: Blowfish, Twofish, Advanced Encryption Standard (AES, Rijndael), 3DES, Serpent, RSA, etc. It’s important to say that the security of an algorithm is related to the strenght and the size of the key, we will talk about this point in the following.

Why we should use strong cryptography?

Many developers, when have need to encrypt some information, try to implement their personal cipher using different approaches. In internet you can find many implementation in PHP of homemade ciphers.

I strongly discourage the use of these homemade ciphers, let me tell you why. Create a secure cipher it’s a very complicated stuff and if you are not a real expert I would say that is quite an impossible task. Moreover here we are talking about strong cryptography, that means even if you will be able to create a new cipher you need time to prove that it is secure, sharing the implementation of the algorithm in the open source community (if you are thinking that the security of your cipher is related to the policy of not share the details, please continue to read the post).

Strong cryptography in PHP

PHP offers different implementations of the most important cryptographic algorithms. In particular PHP has the following cryptographic extensions:

  • Hash
  • mcrypt
  • OpenSSL

The Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2. This extension replace the old mhash extension. With this extension you can generate hash values or HMAC (Hash-based Message Authentication Code). These extension support the most common hash algorithms used in strong cryptography. If you want to know which algorithms are supported by your PHP environment you can use the function hash_algos() that gives a list of all the algorithms supported. For more info about this extension refer to the PHP manual, click here.

The mcrypt extension is an interface to the mcrypt library, which supports a wide variety of block algorithms such as DES, 3DES, Blowfish (default), 3-WAY, SAFER-SK64, SAFER-SK128, TWOFISH, TEA, RC2 and GOST in CBC, OFB, CFB and ECB cipher modes. This extension is the most used in PHP to encrypt data using simmetric ciphers. For more info about this extension refer to the PHP manual, click here.

The OpenSSL extension uses the functions of the OpenSSL project for generation and verification of signatures and for sealing (encrypting) and opening (decrypting) data. You can use OpenSSL to protect data using public key cryptography with the RSA algorithm. For more info about this extension refer to the PHP manual, click here.

Best practices in PHP

So far, we have discussed of general aspects of strong cryptography. Now I would like to give you some best practices on how to use strong cryptography in PHP. Here you can find some of my recommendations:

Use standard algorithms
Always use a standard algorithm to encrypt your data. Don’t try to implement your homemade cipher, you will spend a lot of time and energy without obtain a real security. My suggestion is to use the best algorithms available. Example of algorithms cryptographically strong are:
– Simmetric-key algorithms: AES, that is a FIST 197 standard since 2001;
– Public-key algorithms: RSA, an industry standard algorithm used in many products;
– Hash functions: SHA-x, where x can be 1,256,384, and 512. SHA is a NIST standard.

Key space
When we talk about security of a cipher the key space is one of the most important parameter. If no explicit design strength is give by a cipher, the design strength is equal to the key size. For instance, the DES cipher uses 56-bit key, that means the key space is 2^56. This numbers seems to be hugh but modern computers can effort it, and EFF proof that in the 1998 with the DES cracker. So key space is very important. For symmetric ciphers I would say that 128 bit is the minimum size for a strong cryptography key. Regarding public-key cryptography, experts reccomend a minimum size of 2048 bit or so if we want to protect our data for 20 years.

Kerchoof’s principle
Auguste Kerckhoffs was a Dutch linguist and cryptographer who was professor of languages at the School of Higher Commercial Studies in Paris in the late 19th century. He wrote, in a famous article of “le Journal des Sciences Militaires”, the following sentence, that is considered a must in the modern cryptography:

A cryptosystem should be secure even if everything about the system, except the key, is public knowledge

Using the Shannon interpretation “The enemy knows the system”. In my opinion, in the software world you can have security only with the usage of open source algorithms. If the source code has been tested by thousands of people around the world the probability to find a security bug and consequently a security fix is higher using open source software compared to the usage of closed source.

Don’t use rand() or mt_rand()
Even for this point, don’t try to implement your random generator. You cannot implement a secure random number generator using the rand() function or the mt_rand() function of PHP. The rand() function uses the libc library to generate pseudo-random numbers that is not secure for cryptography applications. It generates random numbers using a linear additive feedback method, with a short period, that is predictable. Even the mt_rand() function is not secure from a cryptographically point of view. It uses the Mersenne Twister algorithm to generate pseudo random numbers. This function is better than the rand() because it faster and it produces pseudo random numbers with a biggest period but is still a deterministic algorithm so is predictable. To generate a cryptographically strong random number in PHP you have to use the function openssl_random_pseudo_bytes() of the OpenSSL library. This function is available starting from PHP 5.3 if you are using an oldest version of PHP you can use these implementation:

function secure_rand($length) {
  if(function_exists('openssl_random_pseudo_bytes')) {
    $rnd = openssl_random_pseudo_bytes($length, $strong);
    if ($strong === TRUE)
      return $rnd;
  }
  $sha =''; $rnd ='';
  if (file_exists('/dev/urandom')) {
    $fp = fopen('/dev/urandom', 'rb');
    if ($fp) {
        if (function_exists('stream_set_read_buffer')) {
            stream_set_read_buffer($fp, 0);
        }
        $sha = fread($fp, $length);
        fclose($fp);
    }
  }
  for ($i=0; $i<$length; $i++) {
    $sha  = hash('sha256',$sha.mt_rand());
    $char = mt_rand(0,62);
    $rnd .= chr(hexdec($sha[$char].$sha[$char+1]));
  }
  return $rnd;
}

In this implementation I used to hash the mt_rand() outputs. This method improves the security of the Mersenne Twister, because it uses the /dev/urandom entropy source (available on GNU/Linux environments), but is not the same level of security of the OpenSSL implementation.

Use bcrypt or scrypt to store user’s password
If you are using a hash function to protect data, for instance a user’s password, you are using an insecure method. Even if you are using a salt to generate the hash is not secure anymore. A random salt will protect your data from Dictionary attacks but unfortunately, today, the power of the modern CPUs and GPUs are able to brute-force passwords in hours/days (see this article New 25 GPU Monster Devours Passwords In Seconds). A secure way to store a password is to use the bcrypt algorithm. PHP offers a bcrypt implementation in the crypt() function. The syntax to use the bcrypt algorithm is as follow:

$salt = substr(str_replace('+', '.', base64_encode($salt)), 0, 22);
$hash = crypt($password, '$2a$' . $cost . '$' . $salt);

where $cost is the base-2 logarithm of the iteration count (Blowfish). Must be on range 04-31.
To check if a given $password string is valid, for a given hash, you can use the following condition:

$hash == crypt($password, $hash)

You can also use the scrypt algorithm in PHP but you need first to install this Scrypt extension.

Size and strength of the passwords
Don’t give the possibility, to the users of your web application, to choose small and dummy passwords. You should always use password with enough characters, I suggest al least 10. You can use the CrackLib library to test the “strength” of a password.

Don’t use plaintext passwords as key for ciphers
A good practice in cryptography, using simmetric ciphers, is to use an hashed value as key of a cipher. This method improves the security of the encrypted data by adding more randomness. That means if you want to generate a good key for a cipher you should use the hash of the password and use that as key of your encryption algorithm.

Use Base64 to encode encrypted data
If you need to exchange encrypted data with different systems, for instance, trasmitting data over internet, is reccomended to encode the data in Base64. In PHP you can use the functions base64_encode() and base64_decode(). This encoding will garantee that your data will be stored correctly independently of the encoding system used in your environment.

Of course, these are only general suggestions and the points related to the correct use of cryptography are more than these. If you are interested in applied cryptography I strongly suggest to read the books of Bruce Schneier: Applied Cryptography, Practical Cryptography, and Cryptography Engineering. Stay tuned on my blog because I’m going to post more articles about Cryptography in PHP with real examples on how to encrypt files, how to store encrypted data in database, how to encrypt session data, etc.