Model » Field encryption

Easily encrypt values before sending for storage within MongoDB with Mondoc.

Encrypting sensitive data, with Mondoc

Occasionally, you may want to encrypt a field before sending it to MongoDB. This is useful for data that is sensitive, or otherwise needs to be protected.

Some examples of this are:

  • Credit/debit card details
  • Date of birth
  • Social security number
  • Or other personally identifiable information (PII)

With this in mind, Mondoc has introduced the ability to declare encryption against individual fields.

To benefit from this, the absolute base Mondoc model, the MondocAbstractSubModel, now contains the MondocEncryptedFieldsTrait.

When installing via Composer, Mondoc will also require the Mondoc Encryption library. This library contains two encryption adapters, as well as a NullAdapter for use when testing.

The two encryption adapters are:

  • AES256Adapter - This adapter uses the AES-256 encryption algorithm.
  • SecretBoxAdapter - This adapter uses the SecretBox encryption algorithm.

Both of these adapters are held within the \District5\MondocEncryption\Adapters\Sodium\ namespace.

You can easily create your own encryption adapter by implementing the MondocEncryptionInterface within the core Mondoc library.

Required extension

Please note that the PHP extension sodium is required to use any of the included adapters.

The interface only requires you to implement the encrypt and decrypt methods.

    public function encrypt(string $field, mixed $value): mixed;
    public function decrypt(string $field, mixed $value): mixed;
Note:

For testing, you may wish to use the NullAdapter, which does not encrypt or decrypt the data. This is useful for testing.

If data is encrypted, you cannot switch to a different adapter without decrypting the data first. This must be done within your code, and not within the Mondoc library itself. A suggested tactic would be to create a sister model, and copy the data from the old model to the new model.

Using field encryption

This example assumes you're using the default AES256Adapter.

Step 1: Set up the adapter
<?php
$myEncryptionKey = \District5\MondocEncryption\Adapters\Sodium\AES256Adapter::generateKey();
// or use your own, which must be 32 bytes
// $myEncryptionKey = '01234567890123456789012345678901'; // 32 bytes

$adapter = new \District5\MondocEncryption\Adapters\Sodium\AES256Adapter(
    \District5\MondocEncryption\Adapters\Sodium\AES256Adapter::generateKey(),
    \District5\MondocEncryption\Adapters\Sodium\AES256Adapter::generateNonce(16)
);

// make sure you know this
$key = $adapter->getEncryptionKey();
$nonce = $adapter->getNonce();
Step 2: Notify MondocConfig about the adapter
<?php
// $adapter is from the previous code snippet

\District5\Mondoc\MondocConfig::getInstance()->setEncryptionAdapter(
    $adapter
);
Step 3: Use encryption in your model(s)

You can encrypt fields within your top level model by declaring the $mondocEncrypted property.

<?php
class MyModel extends \District5\Mondoc\Db\Model\MondocAbstractModel
{
    /**
     * @var string
     */
    protected string $name;

    /**
     * @var DateTime
     */
    protected DateTime $dateOfBirth;

    /**
     * @var string
     */
    protected string $socialSecurityNumber;

    /**
     * @var string[]
     */
    protected array $mondocEncrypted = [
        'dateOfBirth',
        'socialSecurityNumber',
    ];

    // add any getters/setters you need below
}
Step 4: Use encryption in your nested model(s)

Mondoc supports encryption of nested models, or nested model data. In fact, any data type can be encrypted.

If you haven't read the description Nested models documentation, you should do so now. This is a very important part of Mondoc, and essential for many applications.

There are two ways to encrypt nested models, either in the nested model, or .

You can encrypt individual fields within a nested model by declaring the $mondocEncrypted property.

<?php
class DemographicData extends \District5\Mondoc\Db\Model\MondocAbstractSubModel
{
    /**
     * @var string
     */
    protected string $religion;

    /**
     * @var string[]
     */
    protected array $mondocEncrypted = [
        'religion',
    ];

    // add any getters/setters you need below
}
Please note:

This declaration is made within the nested model, not the parent model. So only the $religion property will be encrypted.

Encrypting the entire nested model is easily done by adding the nested model property name to the $mondocEncrypted property on your parent model.

<?php
class MyModel extends \District5\Mondoc\Db\Model\MondocAbstractModel
{
    protected string $name = '';

    protected string $socialSecurityNumber = '';

    protected DemographicData|null $demographicData = null;

    protected array $mondocEncrypted = [
        'socialSecurityNumber',
        'demographicData',
    ];

    // add any getters/setters you need below
}

This will encrypt the entire nested model, including all of its fields. The example nested model may look like this:

<?php
class DemographicData extends \District5\Mondoc\Db\Model\MondocAbstractSubModel
{
    protected string $religion = '';

    protected array $mondocEncrypted = [
        'religion',
    ];

    // add any getters/setters you need below
}
Please note:

Unlike the previous example, in this instance of DemographicData we have omitted the $mondocEncrypted property.

Good to know...

As with all nested model functionality, Mondoc supports encryption of fields or models at an infinite (in theory) depth.

So you can encrypt a field within a nested model, that's within a nested model, that's within a nested model, and so on.

Real examples of this include:

  • Encrypting a CreditCard model, that's within a Payment model, which is contained within a Transaction model.
  • Encrypting a $cardNumber and $securityCode within a CreditCard model, that's within a Payment model, which is contained within a Transaction model.
You may break your queries

Encryption will cause your queries to break. This is because the data is encrypted before it is sent to MongoDB, and therefore makes it impossible to query against.

Creating a custom encryption adapter

Note:

The below example must not be used in production. It is a bare-bones example of how to create a custom adapter that implements the MondocEncryptionInterface.

<?php

use District5\Mondoc\Extensions\FieldEncryption\MondocEncryptionInterface;

class MyEncryptionAdapter implements MondocEncryptionInterface
{
    private array $encryptionConfig;

    public function __construct(array $encryptionConfig)
    {
        // Not required by the interface
        $this->encryptionConfig = $encryptionConfig;
    }

    public function decrypt(string $field, mixed $value): mixed
    {
        return $value;
    }

    public function encrypt(string $field, mixed $value): mixed
    {
        // Do not copy and use this, it does not encrypt
        return $value;
    }
}

And don't forget to register your adapter with MondocConfig:

<?php

\District5\Mondoc\MondocConfig::getInstance()->setEncryptionAdapter(
    new MyEncryptionAdapter([
        /* config */
    ])
);

Good to know!

info You can combine Mondoc field encryption with Mondoc field aliases.

Read more »