This week we will provide some examples for encrypting data with PHP. Data encryption is necessary to protect sensitive information such as passwords, credit card numbers, medical information, etc. PHP provides some easy to use functions to encrypt data so that it can be stored securely.
There are two common but different scenarios for storing sensitive data in an unreadable and protected state; encrypted using a symmetric or asymmetric algorithm or hashed using a hash function. Credit card numbers provide a typical scenario for data encryption because the numbers might later need to be decrypted and used in future transactions. Passwords or answers to security questions provide a typical scenario for data hashing. A hash function is one way and cannot be reversed unless there are vulnerabilities (collisions) in the algorithm. A password only needs to be validated against what a user has supplied to confirm authentication, it does not usually need to be “decrypted” or “reversed”.
Now that we have provided a very high-level and broad overview of the types and reasons for encrypting data, let’s begin identifying the requirements and solutions using PHP…
ASVS 7.1 Requirement:
Verify that all cryptographic functions used to protect secrets from the application user are implemented server side.
ASVS 7.1 Solution:
ASVS 7.2 Requirement:
Verify that all cryptographic modules fail securely.
ASVS 7.2 Solution:
Encryption should fail closed so that data does not get stored in an unprotected state. Implement functionality within your applications to ensure that an error is generated when the encryption/decryption/hashing function fails and do not store/transmit the data (stop processing of the data).
ASVS 7.3 Requirement:
Verify that access to any master secret(s) is protected from unauthorized access (A master secret is an application credential stored as plaintext on disk that is used to protect access to security configuration information).
ASVS 7.3 Solution:
The data is only as secure as the secret, key, passcode, passphrase, password, etc used to encrypt it. Store this value in as few places as possible and provide access to a minimum number of people. Ideally, the value should only be stored in an encrypted backup and on the server on which the application resides.
Protect the value on the application server by storing it in a file with limited permissions. Only the Apache user and group should be provided with access to this file and the access should be limited to read only. Access and changes to this file should be monitored.
The secret/key/passcode/passphrase/password/etc should be rotated on at least an annual basis. This can be a full rotation where existing data is decrypted with the old value and then encrypted with the new one or partial where only new data is encrypted with the new value.
ASVS 7.4 Requirement:
Verify that password hashes are salted when they are created.
ASVS 7.4 Solution:
We have already covered hashes in previous posts. Here we discussed hashing passwords using a salted value to protect against rainbow tables and other attacks. The code for reference:
// Create the salt. First generate the size, then create the salt // and encode it $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $iv = base64_encode(mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM)); // Now we hash the password with Blowfish, salted with the IV created // above, using a work factor of 12: $user_pass = base64_encode(crypt($_POST['user_pass'], "$2y$12$" . $iv));
ASVS 7.5 Requirement:
Verify that cryptographic module failures are logged.
ASVS 7.5 Solution:
Implement logic within the application such that errors are caught and logged when encryption algorithms or hash functions are used. These logs should be reviewed on a regular basis.
ASVS 7.6 Requirement:
Verify that all random numbers, random file names, random GUIDs, and random strings are generated using the cryptographic module’s approved random number generator when these random values are intended to be unguessable by an attacker.
ASVS 7.6 Solution:
Many algorithms and protection mechanisms have been broken due to the fact that they were relying on a pseudo random number generator that could be predicted with enough data. PHP provides the openssl_random_pseudo_bytes random number generator. The code for assigning a random value to a variable is simple:
$random = openssl_random_pseudo_bytes();
A length can be passed to the function as well to generate a string of random bytes with the number of bytes determined by this length parameter.
ASVS 7.7 Requirement:
Verify that cryptographic modules used by the application have been validated against FIPS 140-2 or an equivalent standard. (See http://csrc.nist.gov/groups/STM/cmvp/validation.html).
ASVS 7.7 Solution:
The mcrypt functionality within PHP can be used on Linux, Unix, and Windows based systems. This set of functions supports the AES algorithm and SHA functions. AES is a FIPS 140-2 compliant algorithm and SHA-1 or greater functions are compliant secure hash standards. Use AES for encryption and SHA-1 or greater for hashing and you will be FIPS 140-2 compliant (at least for now).
ASVS 7.8 Requirement:
Verify that cryptographic modules operate in their approved mode according to their published security policies (See http://csrc.nist.gov/groups/STM/cmvp/validation.html).
ASVS 7.8 Solution:
I don’t believe that the application developer has much of a choice here when using PHP. The advice here would be to use one of the FIPS 140-2 algorithms, with the correct size (128/192/256 bit, etc), correct mode (CBC is suggested as it is commonly supported, used, and more secure than some other methods. See more here), and strong key/secret.
I am going to skip 7.9 as it is policy related and could vary depending upon the application, data to be protected, and company protecting the data.
ASVS 7.10 Requirement:
Verify that all code supporting or using a cryptographic module is not affected by any malicious code.
ASVS 7.10 Solution:
Scan your code for viruses, or any malicious backdoors. Perform manual and automated code reviews.
And now for some code
The mcrypt_encrypt and mcrypt_decrypt functions can be used to encrypt and decrypt data respectively and the mcrypt_get_iv_size and mcrypt_create_iv functions can be used to generate an Intialization Vector (IV) to be used by the encryption/decryption functions.
The mcrypt_get_iv_size requires two variables; the cipher in use and the mode. The mcrypt_create_iv function also requires two variables; the IV size (obtained with mcrypt_get_iv_size) and the random source. Source to create the IV:
// Get the IV size for AES 128 using the CBC mode and store it in // a variable: $ivsize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); // Create the IV from the IV size obtained before and store it in // a variable. Use the MCRYPT_RAND cross-platform source for // randomness: $iv = mcrypt_create_iv($ivsize, MCRYPT_RAND);
The mcrypt_encrypt and mcrypt_decrypt functions both take five variables; cipher for encryption, key/secret used to encrypt the data, data to be encrypted, mode of encryption, and IV. Source to encrypt data:
// Read the key file into a variable $key = file_get_contents('/etc/keys/webapp.key'); // Unencrypted data to protect $plaintext = "asfadsfadsfadsfa"; // Encrypt the data using the AES-128 algorithm, // the key above, the CBC mode, and the IV // generated earlier: $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
Code to decrypt data:
// Read the key file into a variable $key = file_get_contents('/etc/keys/webapp.key'); // Encrypted data to decrypt $ciphertext = "ohafosih9fh29/'09j0"; // Decrypt the data using the AES-128 algorithm, // the key above, the CBC mode, and the IV // generated earlier: $plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv);
When storing encrypted data and IV’s, it is sometimes best to encode the data after encryption. Some systems don’t handle processing or storage of encrypted data well. Encoding the encrypted data solves this problem. Change the encryption code to the following to encode the data:
$ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv));
Change the above decryption code to the following:
// This example assumes that the data and IV were both // retrieved as encoded values $plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($ciphertext), MCRYPT_MODE_CBC, base64_decode($iv));
That should just about do it. The above examples should get you on the way towards protecting sensitive data within your applications and data stores.
Leave a Reply