📜 ⬆️ ⬇️

PHP Data Encryption Guide

From the translator: in the process of programming, I never forget that I am dangerously incompetent in cryptography , and I advise everyone to proceed from this thesis (well, maybe apart from you and another cool guy). However, one way or another, in the process of work there are tasks related to the protection of data, and they must be solved. Therefore, I offer you the translation of the article by Finnish developer Timo H , which seemed to me quite interesting and useful.

Important update : in the comments SamDark made a remark that the Mcrypt library has not been supported for a long time and has a number of flaws, therefore it is recommended to use OpenSSL. If you need to rewrite the existing code, this article can help. In addition, there is evidence that Mcrypt can be removed in PHP7 .

This is a quick tutorial on how to avoid common errors with symmetric encryption in PHP.
')
We will consider the case when data is processed on the server side (in particular, encryption occurs on the server, and data can be received, for example, from the client in the form of clear text, password, etc.), which is a typical case for PHP applications .

The information in this guide should not be used to create encrypted network connections that have more complex requirements. For such cases, you must use spiped or TLS .

Naturally, the recommendations given here are not the “only possible way” to organize encryption in PHP. The goal of this guide is to try to leave less room for mistakes and complex ambiguous solutions.

PHP encryption features


Use Mcrypt or OpenSSL extensions.

Encryption algorithm and its mode of operation, one-time code (initialization vector)


Use AES-256 in CTR mode with a random one-time code ( approx. Trans .: nonce ). AES is a standard, so you can use the functions of any of the extensions - Mcrypt or OpenSSL.

Always generate a new one-time code. In this case, you must use a cryptographically stable source of random numbers. A little more about the generation of random numbers, see below. The one-time code is not a secret, and can be concatenated with a ciphertext for transmission and subsequent decryption.

The one-time code must be 128 bits long (16 bytes), just a string of bytes without any encoding.

The Mcrypt AES extension is known as Rijndael-128 ( this is not an error. AES-256! = Rijndael-256 ) , although it’s about AES-256 . In OpenSSL, respectively, AES-256-CTR.

An example of using Mcrypt:
<?php // $key length must be exactly 256 bits (32 bytes). // $nonce length must be exactly 128 bits (16 bytes). $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, 'ctr', $nonce); // Mcrypt 

OpenSSL usage example:
 <?php // $key length must be exactly 256 bits (32 bytes). // $nonce length must be exactly 128 bits (16 bytes). $ciphertext = openssl_encrypt($plaintext, 'AES-256-CTR', $key, true, $nonce); // OpenSSL 

Make sure that the encryption works correctly using test vectors ( note re.: For AES-256-CTR, see clause F.5.5 on page 57 ).

For CTR mode, there are some restrictions on the total amount of encrypted data. Perhaps you will not encounter this in practice, but keep in mind that it is not necessary to encrypt more than 2 ^ 64 bytes of data with one key, without any message being one long message or many short ones.

CTR mode retains durability only if you do not use the same one-time code with the same key. For this reason, it is important to generate one-time codes using a cryptographically strong source of randomness. In addition, this means that you should not encrypt more than 2 ^ 64 messages with one key. Since the length of a one-time code is 128 bits, it is important to limit the number of messages (and the corresponding one-time codes) to 2 ^ 128/2 due to the Birthdays paradox ( comment perev .: more about the paradox ).

And remember that encryption will not hide the fact how much data you send. As an example of an extreme case, if you encrypt messages containing only “yes” or “no”, obviously, encryption will not hide this information.

Data authentication


Always perform authentication and data integrity.
To do this, use MAC after encryption. Those. first, the data is encrypted, and then the HMAC-SHA-256 is taken from the received ciphertext, including the actual ciphertext and one-time code.

When deciphering, first test the HMAC using a time-resistant attack comparison algorithm. Do not directly compare $ user_submitted_mac and $ calculated_mac using comparison operators == or ===. It is better to even use the " double check HMAC ".

If the HMAC test succeeds, it can be safely decrypted. If the HMAC does not fit, shut down immediately.

Encryption and authentication keys


Ideally, use keys derived from a cryptographically secure source of randomness. AES-256 requires 32 bytes of random data (the “raw” line is a sequence of bits without using any encoding).

If you rely on a key ( comment perev .: the key entered by the user is called "password" ) entered by the user or specified in the configuration, then it needs to be converted before being used as an encryption key. Use PBKDF2 to turn the password into an encryption key. Read more http://php.net/hash_pbkdf2 .

If the application is running under PHP version below 5.5, where there is no PBKDF2 embedded implementation, you will have to use your own PHP implementation, an example of which can be found here: https://defuse.ca/php-pbkdf2.htm . Keep in mind that, relying on your own implementation, you may not be able to convert the key properly, as the built-in function hash_pbkdf2 () does.

Do not use the same key for encryption and authentication. As stated above, 32 bytes per encryption key and 32 bytes per authentication key (HMAC) are required. With PBKDF2, you can get 64 bytes from a password and use, say, the first 32 bytes as the encryption key, and the remaining 32 bytes for the authentication key.

If your passwords are stored in a file, for example, in the form of a HEX string, do not re-encode them before feeding the encryption functions. Instead, use PBKDF2 to convert keys from HEX encoding immediately to a quality encryption or authentication key. Or use SHA-256 with output without additional coding (just a string of 32 bytes) for hashing passwords. Using regular password hashing gives you enough entropy. Details are described in the following paragraphs.

Key Stretch


First, the use of low entropy keys should be avoided. But still, if you need to use, for example, user passwords, you should definitely use PBKDF2 with a large number of iterations to maximize the security of the key.

One of the parameters for PBKDF2 is the number of hash iterations. And the higher it is, the more key security can be expected. If your code works on a 64-bit platform, use SHA-512 as a hashing algorithm for PBKDF2. For a 32-bit platform, use SHA-256.

However, it is not possible to use a relatively high number of iterations in online applications due to the risk of a DoS attack. Therefore, the key quality will not be as high as in offline applications that can afford a large number of iterations without such a risk. As a rule, for online applications, such a number of hashing iterations is selected so that PBKDF2 can work no more than 100 ms.

In case you can use passwords with high entropy, it is not necessary to carry out a “stretch”, as for passwords with low entropy. For example, if you create the "master_key_ encryption" and "master_key_autification" using / dev / urandom, then the need for PBKDF2 is completely eliminated. Just be sure to use the keys as bit sequences, without any coding.

In addition, using PBKDF2 makes it easy to get both keys for encryption and authentication from one master password (just use a small number of iterations or even one). This is useful if you have only one “master password” used for both encryption and authentication.

Key Storage and Management


The best thing is to use a separate, dedicated key storage device ( HSM ).

If this is not possible, then to complicate the attack, you can use file encryption with keys or a configuration file (in which the actual encryption / authentication keys are stored) using a key stored in a separate place (outside the home directory or the site root). For example, you can use the Apache environment variable in httpd.conf to save the key needed to decrypt the file with the actual keys:
 <VirtualHost *:80> SetEnv keyfile_key crypto_strong_high_entropy_key # You can access this variable in PHP using $_SERVER['keyfile_key'] # Rest of the config </VirtualHost> 

Now, if the files in the root of the site and below, including files with keys, are compromised (for example, if a backup is leaked), the encrypted data will remain intact because the key stored in the environment variable has not been compromised. It is important to remember that the httpd.conf files should be backed up separately, and not compromise the keyfile_key variable through, for example, the output of phpinfo ().

If instead of a configuration parameter you use a file, then it is possible to organize the rotation of keys. In the worst case, if the adversary has acquired your encryption and authentication keys, and this fact has gone unnoticed, the rotation of the keys with some frequency can limit its access (provided that it cannot acquire the new keys). This technique will help reduce the damage, because the enemy will not be able to use the compromised keys indefinitely.

Data compression


In general, you should not compress the source text before encryption. This may give the opponent an additional analysis tool.

For example, if you store session data in an encrypted cookie, with some of this data provided by the user, and some of the secret information, the adversary may learn additional information about the secret by sending as a regular user certain data generated and measuring how the length of the received ciphertexts varies.

Text is compressed more efficiently if there are duplicate sections. By manipulating user data, you can choose so that they partially coincide with secret data. The greater the match, the smaller the size of the ciphertext will be output. This type of attack is called CRIME .

If you do not have a hard need to compress the data, do not compress.

Server environment


As a rule, you should not place security-demanding applications on a shared server. For example, on a shared hosting, where an adversary can access a virtual machine on the same physical server as you.

There are various reasons that make shared servers a questionable place to host security-critical applications. For example, attacks between virtual servers were recently demonstrated: eprint.iacr.org/2014/248.pdf . This is a good reminder that attack techniques do not degrade, but rather sharpen and improve over time. You should always take into account such pitfalls.

Expert advice


Last but not least, consult with an expert, let him review your security code.

@rootlabs , June 5, 2014:
@plo @veorq I have been working in cryptography since 1997, and so far all my decisions and implementations are reviewed by a third party.

Cryptographically resistant random numbers


Use a random source provided by the OS. In PHP, for example, mcrypt_create_iv ($ count, MCRYPT_DEV_URANDOM) or read directly from / dev / urandom.

Always check that the correct number of bytes is received from the source. If this is not the case, do not attempt to correct this error yourself using self-made pseudo-randomness algorithms.

Source: https://habr.com/ru/post/257439/


All Articles