Welcome Guest

Laminas Doctrine Entity Encryption/Decryption Module

Doctrine Crypt is a simple data encryption/decryption module for your Doctrine entities. This module automates this process by simply listening for database select, insert and update events and reading from your configuration to determine which entities and their properties to process.

This module requires Doctrine Module and uses Doctrine ORM. I also assume that you are familiar with Doctrine ORM, Doctrine Module, Laminas / Zend Framework and using the command line interface.

This was originally an addition to my Doctrine Auth module but has been split and improved in this module. As of v0.4 of Doctrine Auth the encryption has been removed, to use encryption you will need to install this module in your application.

Doctrine Crypt uses Block Cipher AES 256bit encryption by default but you can change this to RSA key encryption if you wish. RSA encryption is restricted in the length of the data you can encrypt. I have implemented RSA encryption as my Doctrine Auth module prior to 0.4 used RSA encryption as an option. This will allow users of this encryption method to decrypt their database and encrypt with Block Cipher if they wish.

Doctrine Crypt also comes with three command line scripts to encrypt, decrypt or to change the encryption method on an existing database using your configuration settings. This will allow you to easily secure your existing database records. Encrypting an unencrypted is not a necessary step though as Doctrine Crypt will start encrypting your records once you start saving them using `\Doctrine\ORM\EntityManager::flush()`. This module can detect if your data is encrypted so it simply passes non-encrypted values through to your entity object.

Unfortunately this module can only process entities automatically as hydrating the results as an array does not go through Doctrine listeners. If you need to access the data as an array you can pass the results array to the Crypt models decryptArray() method, this is not advised for large datasets. The Crypt model also has encrypt() and decrypt() methods to process data manually. There is also a decrypt() view helper as well.

Table of contents

Installation Return to top

With Composer Return to top

  1. In the root of your application enter:
    Copy to clipboard $ composer require krytenuk/doctrine-crypt

Post installation Return to top

  1. If you have the laminas-component-installer plugin you will be prompted to add the module to your application. If not the simply add it to your `config/modules.config.php` file. <?php
        
    return array(
            
    'modules' => array(
                
    // ...
                
    'FwsDoctrineCrypt',
            ),
            
    // ...
        
    );
  2. Copy `./vendor/krytenuk/doctrine-crypt/config/doctrine-crypt.local.php.dist` to `./config/autoload/doctrine-crypt.local.php`.

Configuration Return to top

This module is simply run using the configuration to determine the method of encryption and the entities and their properties to be encrypted/decrypted. Listeners setup in Doctrine will automatically encrypt and decrypt as required.

In your `config/autoload/doctrine-crypt.local.php`

Please check the configuration settings when updating Doctrine Crypt as settings may be added or removed in future releases causing errors in your application

All configuration settings are under the `doctrineCrypt` key

Key
Description
encryptionMethod
The encryption method to use, either Block Cipher or RSA. Use either one of `\FwsDoctrineCrypt\Model\Crypt::CRYPT_BLOCK_CIPHER`(default) or `\FwsDoctrineCrypt\Model\Crypt::CRYPT_RSA`

Block Cipher encryption setup

Key
Description
encryptionKey
This is the 256bit key used for AES block cipher encryption. This ideally should be a 32 character random string. You can generate one by going to, https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx, and selecting 256bit.

RSA public key cryptography setup

Key
Description
rsaPrivateKeyFile
This is your RSA private key file. Please see below for information on how to generate a key pair. The default value is `rsa/key.pem`, this path is relative to your application root.
rsaPublicKeyFile
This is your RSA public key file. Please see below for information on how to generate a key pair. The default value is `rsa/key.pub`, this path is relative to your application root.
rsaKeyPassphrase
Optional passphrase used when creating the above keys.

Entities configuration

This is where you specify which entities and their properties you wish to process. By adding them here Doctrine Crypt will automatically encrypt/decrypt the records as required. It’s as simple as that!

Key
Description
entities
A numerical array of entities to process.

Each entity configuration element is an array containing the following elements.

Key
Description
class
The fully qualified class name of your entity, e.g. `\YourModule\Entity\YourEntity::class`
properties
A numerical array containing the entities properties to encrypt/decrypt.

Here is an example configuration.

<?php

use YourModule\Entity\User;
use 
YourModule\Entity\UserAddress
use FwsDoctrineCrypt\Model\Crypt;

return [
    
'doctrineCrypt' => [
        
/** 
        * Use one of Crypt::CRYPT_BLOCK_CIPHER (recommended) or Crypt::RSA
        * @see https://docs.laminas.dev/laminas-crypt/block-cipher/
        * @see https://docs.laminas.dev/laminas-crypt/public-key/#rsa
        */
        
'encryptionMethod' => Crypt::CRYPT_BLOCK_CIPHER,
        
// for Block Cipher encryption (comment out or remove if not using Block Cipher AES encryption)
        
'encryptionKey' => 'your_encryption_key',
        
// for RSA encryption (comment out or remove if not using RSA encryption)
        
'rsaPrivateKeyFile' => 'rsa/key.pem',
        
'rsaPublicKeyFile' => 'rsa/key.pub',
        
'rsaKeyPassphrase' => 'YourPassphrase',
        
'entities' => [
            [
                
'class' => User::class,
                
'properties' => [
                    
'firstName',
                    
'surname',
                    
'companyName',
                ],
            ],
            [
                
'class' => UserAddress::class,
                
'properties' => [
                    
'address',
                    
'town',
                    
'county',
                    
'postCode',
                ],
            ],
            
// ...
        
],
    ],
];

Creating your RSA encryption/decryption keys Return to top

In order to use RSA public key cryptography you need to create an RSA key pair, not required if using Block Cipher encryption. This can easily be done with openssl, to generate your keys with openssl start with the following command.

Copy to clipboard $ openssl genrsa -out key.pem 2048

This creates your private key, "key.pem" file. Also you will be asked for a passphrase, please enter this in the configuration. Please note a passphrase is optional but recommended. To extract your public key and create your "key.pub" file simply use.

Copy to clipboard $ openssl rsa -in key.pem -pubout > key.pub

Don’t forget to copy your keys to the folder you specified in the configuration. For more information please see https://en.wikibooks.org/wiki/Cryptography/Generate_a_keypair_using_OpenSSL.

Command line tools Return to top

Doctrine Crypt also has a couple of command line scripts to encrypt or decrypt your existing database records. These commands read the configuration so all you need to do once you have set you entities is to run the relevant command.

Ensure you backup your database first.

To encrypt your database records.

Doctrine Crypt will attempt to determine if any of your your records have already been encrypted, if so the record(s) are skipped. This is not guaranteed however, so it is not advised to run this command on a already encrypted database, decrypt first.

To perform a test run, no records are saved.

Copy to clipboard $ ./vendor/bin/doctrine-module doctrine-crypt:encrypt  --dry-run

To encrypt and save records.

Copy to clipboard $ ./vendor/bin/doctrine-module doctrine-crypt:encrypt

To decrypt your database records.

To perform a test run, no records are saved.

Copy to clipboard $ ./vendor/bin/doctrine-module doctrine-crypt:decrypt  --dry-run

To decrypt and save records.

Copy to clipboard $ ./vendor/bin/doctrine-module doctrine-crypt:encrypt

To change your database encryption method.

I added this command mainly for those who already use the encryption in my Doctrine Auth module and wish to switch from RSA to Block Cipher encryption. This command will automatically do that using your config settings. If you set the encryptionMethod key in the config to Crypt::CRYPT_BLOCK_CIPHER then this command will decrypt your specified entities using RSA and re-encrypt using Block Cipher. The reverse is also true if you set the encryptionMethod key to Crypt::CRYPT_RSA it will decrypt with Block Cipher and re-encrypt with RSA. To do this you need to ensure both encryption methods are set in your configuration.

To perform a test run, no records are saved.

Copy to clipboard $ ./vendor/bin/doctrine-module doctrine-crypt:re-encrypt  --dry-run

To re-encrypt and save records.

Copy to clipboard $ ./vendor/bin/doctrine-module doctrine-crypt:re-encrypt

This command can also accept two arguments, the decrypt and encrypt methods to use. You must have the encryption/decryption settings in your configuration for this to work. To decrypt using RSA and encrypt using Block Cipher simply use.

Copy to clipboard $ ./vendor/bin/doctrine-module doctrine-crypt:re-encrypt rsa block-cipher

Using Doctrine array hydration Return to top

The Crypt model

As I have mentioned above the Doctrine listeners are not called when using Doctrine module’s array hydration, AbstractQuery::HYDRATE_ARRAY. If you really need the data as an array then you can manually decrypt using the Crypt::decryptArray() method. This method is not recommended for large datasets. The Crypt model is registered as a service and can be retrieved from the container inside your factories invoke method.

As an example, in your factory

<?php

namespace YourModule\Folder\ServiceFactoryPath;

use 
FwsDoctrineCrypt\Model\Crypt;
use 
Doctrine\ORM\EntityManager;
use 
Laminas\ServiceManager\Factory\FactoryInterface;
use 
Psr\Container\ContainerInterface;
use 
YourModule\Folder\YourClass;

class 
IndexControllerFactory implements FactoryInterface
{
    public function 
__invoke(ContainerInterface $container$requestedName, array $options null): IndexController
    
{
        return new 
YourClass(
            
$container->get(Crypt::class),
            
$container->get(EntityManager::class)
        );
    }
}

and in your model or controller

<?php

namespace YourModule\Folder;

use 
FwsDoctrineCrypt\Model\Crypt;
use 
Doctrine\ORM\EntityManager;
use 
Doctrine\ORM\AbstractQuery;

class 
YourClass
{
    
/** 
     * Example using PHP 8.1+ 
     */
    
public function __construct(
        private readonly 
Crypt $crypt,
        private readonly 
EntityManager $entityManager
    
): void
    
{}
    
    public function 
yourFunction()
    {
        
/** Fetch results from database using array hydration */
        
$queryBuilder $entityManager->createQueryBuilder();
        
$resultsArray $queryBuilder->select('t')
            ->
from(YourEntity::class, 't')
            ->
getQuery()
            ->
getResult(AbstractQuery::HYDRATE_ARRAY);
            
        
/** Decrypt the results array */
        
$decrypted $crypt->decryptArray($resultsArray);
    }
}

The Crypt model also has encrypt() and decrypt() methods.

Copy to clipboard <?php

$crypt
->decrypt($encryptedValue);

Copy to clipboard <?php

$crypt
->encrypt($value);

The decrypt view helper

As an alternative to the above it is possible to decrypt data inside you view scripts with the decrypt() view helper. Simply use it inside of you view script or view helper with the content to decrypt.

Inside of a view script

Copy to clipboard <?= $this->decrypt($encryptedValue); ?>;

Inside of a view helper

Copy to clipboard <?php

namespace YourModule\View\Helper;

class 
YourViewHelper extends AbstractHelper
{
    public function 
__invoke()
    }
        
$html '';
        
// ...
        
$html .= $this->view->decrypt($encryptedValue);
        
// ...
        
return $html;
    }
}
Donate