How to Encrypt Data in PHP

PHP's mcrypt functions can be used to encrypt data, but it's not easy to use them correctly. The three most common pitfalls are:

  1. Not using authenticated encryption.

    Programmers who are new to cryptography often assume that just encrypting the data is enough. But that's not true. You have to make sure that nobody can modify with the encrypted data too. If you don't, then you open yourself up to a plethora of attacks.

    To detect ciphertext modification, apply a Message Authentication Code (MAC) (e.g. HMAC) to the ciphertext after encrypting, and check that it is correct before decrypting a ciphertext.

  2. Not unambiguously padding the plaintext.

    Block cipher modes like Cipher Block Chaining (CBC) can only encrypt texts whose length is a multiple of the cipher's block size. To encrypt data of any size, we add "padding" to the data before encrypting, so that its length is a multiple of the cipher's block size.

    Unfortunately, the mcrypt encryption functions just append zero bytes (0x00) to the data until its length is a multiple of the cipher's block size. This is ambiguous, because if you encrypt some data that ends in zero bytes, when you try to decrypt it, you won't be able to tell which zero bytes are part of the original data and which zero bytes are padding. So you have to implement your own padding.

  3. MCRYPT_RIJNDAEL_256 is not AES-256.

    MCRYPT_RIJNDAEL_256 refers to the version of the Rijndael cipher that operates on 256-bit blocks, not the version of the Rijndael cipher that uses 128-bit blocks and 256-bit keys. AES is only the 128-bit block version of Rijndael (which can use 128-, 192-, and 256-bit keys). So MCRYPT_RIJNDAEL_256 is not AES. AES is MCRYPT_RIJNDAEL_128.

    The mcrypt methods determine the key size by the length of the string you pass in for the key. If you give it a 16-byte string, it'll use AES-128; if you give it a 32-byte string, it'll use AES-256.

    Do not use MCRYPT_RIJNDAEL_256. Use MCRYPT_RIJNDAEL_128.

PHP Encryption Library

The following PHP class does encryption and decryption with AES-128 and uses HMAC-SHA256 for authentication. It uses PKCS #7 padding so that decryption will always return a string that is exactly the same as the one that was encrypted.

Warning: Cryptography is very easy to get wrong. It's difficult to overstate how hard it is to do crypto right. If you're new to implementing cryptography, I very strongly recommend asking a professional cryptographer for help. Almost all cryptosystem failures are due to implementation errors.

Source Code on GitHub

If you're looking for more than symmetric encryption and decryption, check out libsodium-php.