Securing Symfony entities using the AES-256

I care about my users. Tech is meant to serve people, not the other way around.

Encryption is an essential component when it comes to securing user data. In a database, encryption prevents information leaks. It can also be used to anonymize all the knowledge gathered in an application, to make it private, personal.

In a word, encrypting is caring.

This article proposes an implementation of the most secured encryption standard available, the Advanced Encryption Standard (AES, also known as Rijndael) over 256 bits, in order to secure the storage of Entity properties in the PHP framework Symfony.

Design choice

Symfony is a MVC framework.

Three possibilities :

  • Encryption service : inject an Encryption service when using the entity's CRUD service
  • Define a new entity annotation
  • Directly inside the Entity : encrypt/decrypt directly from the entity's getters and setters

I choose the last one for three reasons :

  • Accessing encrypted data from Twig will appear no different from accessing unencrypted properties : {{object.property}}
  • It keeps things simple : no additional service layer, just a slightly thicker entity class. Best practices suggest to use an additional service layer, but I don't like the idea of explicitly parsing the data to look for encrypted properties. It's just a personal preference.
  • Defining a new entity annotation would be the cleanest option for the Symfony framework, but it's also the most complex one. I want to give a raw AES implementation, easy to replicate in any PHP program.

Encryption algorithm

In PHP, encryption is managed by the official implementation of the OpenSSL library[1].

PHP not providing multiple inheritance, I resort to a trait[2] to make the code more reusable throughout the web application.

Encryptable.php

<?php
namespace App\Entity;

trait Encryptable{

    /**
      * This function encrypts the input following the Advanced Encryption Standard over 256 bits
      *
      * @param string $data The input data to encrypt
      *
      * @return string the encrypted data
      */
    public function encrypt($data){

        //the cipher method
        $method = 'aes-256-cbc';

        //your app AES password
        $key = $_ENV['AES_PASSWORD'];

        //generate an initialization vector on 16 random bytes (size defined by the AES specification)
        $iv = openssl_random_pseudo_bytes(16);

        //OPENSSL_RAW_DATA makes sure that the function returns a raw binary representation of the data without encoding (by default, the data is base64-encoded so my code here is redundant in order to be clearer)
        //the result is base64-encoded so that it's easier to manipulate the data across any sub-systems
        $encrypted_data = base64_encode(openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv));

        //we need the initialization vector to decrypt the data later
        $result = $encrypted_data . ':' . base64_encode($iv);

        return $result;

    }

    /**
      * This function decrypts the input following the Advanced Encryption Standard over 256 bits
      *
      * @param string $encrypted_data The input encrypted data to decrypt
      *
      * @return string the original data
      */
    public function decrypt($encrypted_data){

        //just the inverse operation

        $method = 'aes-256-cbc';

        $key = $_ENV['AES_PASSWORD'];

        $data_parts = explode(':', $encrypted_data);
        $data = base64_decode($data_parts[0]);
        $iv = base64_decode($data_parts[1]);

        $result = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);

        return $result;
    }
}

Setting up the AES password

Encode a password to be used by the AES algorithm, using Symfony's command line interface :

Picture : Encoding a password using Symfony's command line interface

Obviously, don't hard code the password. Instead, set up a dedicated environment variable :

.env

AES_PASSWORD=<your bcrypted password by Symfony>

Integration to an Entity

Now, all needs to be done is to integrate the trait inside the class of the entity.

Operation.php

<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

//import the trait
use App\Entity\Encryptable;

/**
 * @ORM\Entity
 * @ORM\Table(name="Operation")
 */
class Operation{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @ORM\Column(type="string", length=260)
     */
    private $amount;

    public function __construct(){
    }

    //// GETTERS

    public function getId(){
        return $this->id;
    }

    public function getAmount(){
        // the decrypt() function is ready to use
        return $this->decrypt($this->amount);
    }

    //// SETTERS

    public function setAmount($amount){
        // the encrypt() function is ready to use
        $this->amount = $this->encrypt($amount);
        return $this;
    }

    //// FUNCTIONS

    // integrate the trait
    use Encryptable;
}

The Entity can now be seamlessly manipulated by the service or controller layers.

Conclusion

Your data is now safe !

How the encrypted data looks like :

Picture : Encoded database

And once it's decrypted :

Picture : Decoded result

I hope your users will never have to thank you for this, but at least you can rest assured you did the right thing :)

References :

  1. Official documentation of PHP's OpenSSL encrypt implementation : http://php.net/manual/fr/function.openssl-encrypt.php
  2. PHP official documentation - Trait : http://php.net/manual/en/language.oop5.traits.php
  3. AES-256 encryption and decryption in PHP and C# : https://gist.github.com/odan/138dbd41a0c5ef43cbf529b03d814d7c#file-with_bcrypt_password_hash-md
  4. The Missing PHP AES Encryption Example : https://blog.turret.io/the-missing-php-aes-encryption-example/
  5. Complete specification of the Rijndael Block Cipher : https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/aes-development/Rijndael-ammended.pdf

I am Basile, a young software craftsman documenting his entrepreneurship journey. If you liked this article, you can follow my adventures in real time on Twitter. I’m always looking forward to meeting new people and learning from others !

My personal website : basilesamel.com