Some sites or applications retain sensitive data about users or clients that need to be stored within the database. And though you may feel great about your site’s security, the data that belongs to the website is not always accessible only within the application. For example: database backups contain a copy of all your site data- are your backups secured and encrypted?

Ideally your sensitive data is encrypted at rest within the database, and the data is stored separate from the key used to encrypt it. This post covers the installation and configuration of modules needed to encrypt field data at rest in Drupal 8.

Prefer to watch a video? Click here to jump to my video tutorial.

Goal: Encrypt fields of my choosing within Drupal, and store the encryption key separate from the data behind another layer of protection.

Prerequisites: To make best use of this article, you should be familiar with installing Drupal 8 modules and their dependencies, as well as being comfortable enough within a terminal to execute a few simple commands:

Needs: A secure symmetrical algorithm for encrypting data, a key that is used by the algorithm to encrypt and decrypt the data, and an API that modules can use to leverage the algorithm and key as needed to encrypt data.

Enter a set of encryption related modules for Drupal.

Modules

As with many Drupal tasks, to achieve this goal we’re going to need at least 4 modules: Key, Encrypt, Real AES, and Field Encrypt. Each of these modules performs one specific function that the others utilize. Let’s look at each one and how it fits into the others.

Key

https://www.drupal.org/project/key

Key provides the ability to manage keys, which can be employed by other
modules. It gives site administrators the ability to define how and
where keys are stored, which allows the option of a high level of
security and allows sites to meet regulatory or compliance
requirements.

In other words, the Key module gives you a simple UI within Drupal for managing your various keys for services. Some examples of services that may require a key are:

A password or API key for connecting to an external service, such as
PayPal, MailChimp, Authorize.net, UPS, an SMTP mail server, or Amazon
Web Services. A key used for encrypting data.

On its own, this module doesn’t do much at all. In addition to the Key module we’re going to need another module that makes use of the keys.

Real AES

https://www.drupal.org/project/real_aes

Real AES provides an encryption method plugin for the Encrypt module. This plugin offers authenticated encryption based on AES-128 CBC with a HMAC. It can also serve as a library loader for the Defuse PHP-encryption library.

The Real AES module is what I’ve chosen for the encryption method for this tutorial, but there are others available such as Sodium.

Encrypt

https://www.drupal.org/project/encrypt

This module provides a global encryption service that can be invoked via the
services interface.

The Encrypt module also provides an administrative UI within Drupal for creating reusable global services that leverage your encryption keys in conjunction with an encryption method. In other words, this module will provide an API to other modules so that they can make use of the encryption keys we’ll create with the Key module, and the chosen encryption method (Real AES).

After installation and configuration of the Key and Real AES modules, the Encrypt module allows us to create various “Encryption Profiles” that leverage the Keys and encryption methods of your choice. Once created, these profiles become available to the global encryption service the encrypt module provides. We’ll look at how to use this service more later.

Field Encryption

https://www.drupal.org/project/field_encrypt

Adds options to encrypt field values. The goal of this module is to create a method for encrypting field values when stored in the database.

The Field Encryption module provides a UI for managing the encryption settings for fields within Drupal. After enabling the module, you can edit the Storage Settings on a field and configure the field to encrypt its data within the database, along with some other settings.

Installation and Setup

To get started, the first thing we need to do is download these modules and their dependencies. Using a composer based Drupal codebase, this is as easy as executing one long command in the terminal. Doing it this way will automatically retrieve the Defuse PHP-encryption library required by the Real AES module.

composer require drupal/key drupal/real_aes drupal/encrypt drupal/field_encrypt

Alternatively if you are not setup with a composer based Drupal installation, you will need to download all the Drupal modules using the method you prefer and add the following required libraries to your site’s vendor directory.

After downloading all the modules and their requirements, enable them within Drupal. Now we need to configure each of these modules for use.

Key: Generating a key for use with Real AES

The first thing we need to do is generate an encryption key. The easiest way I’ve found to do this is actually according to the Sodium module documentation. In your terminal execute the following command, but change the path and filename to where you want to store your new key.

dd if=/dev/urandom bs=32 count=1 | base64 -i - > path/to/my/encrypt.key

This generates a 256-bit key that is base64 encoded and saves it as the file indicated in the command. I prefer the base64 encoded approach, as it allows me to easily copy and paste the key.

Best Practice: Though the Key module allows us to store the key within the site’s configuration (database), this is not ideal. Storing your encryption key within your database is like duct taping your house key to your front door. Yes, the door is technically locked, but it’s hardly secure. Instead, we want to store the key somewhere away from the data it is encrypting. Ideally on a different server entirely (more on this later), but at a bare minimum as a file outside the web-root.

Now that we have a key generated, let’s add it to the Key module within Drupal.

  1. Navigate to Configuration > System > Keys (/admin/config/system/keys) and click “Add Key”
  2. Provide your new key with Name and Description that identifies what the key is used for. In my case, I’ve named my key “Field Encryption” and given it the description of “Used to encrypt field data”.
  3. Key Type: choose “Encryption”. This will change the form and provide a new field named “Key Size”.
  4. Key Size: select “256”
  5. Key Provider: choose “File”.
  6. File Location: provide the absolute path to the key you generated above. ie, path/to/my/encrypt.key
  7. Check Base64-Encoded so the module knows this key is encoded.
  8. Save your new Key.

If the form saves without error, then you’re done setting up the Key module 🙌. If you receive an error about the key being an incorrect size, it is likely due to the method in which you generated the key. Try generating your key again using the command mentioned above.

Screenshot

Screenshot of creating a new key

Encrypt: Creating a reusable Encryption profile

Since the Real AES module only provides an encryption method to Drupal, there is no additional setup required for that module and we can move on to configuring the Encrypt module.

Now we need to create an encryption profile that uses our new Key and the Real AES encryption method. Once we do so, that encryption profile becomes available for use by other modules, and also as a service within your code.

  1. Navigate to Configuration > System > Encryption profiles (/admin/config/system/encryption/profiles) and click “Add Encryption Profile”
  2. Create a label for your new encryption profile such as “Field Encryption AES Profile”. Any name will do that allows you to reliably identify this profile.
  3. Encryption Method: choose “Authenticated AES (Real AES)”, and this will reload part of the form, allowing you to select a Key.
  4. Encryption Key: choose the Key you created in the previous section.
  5. Save your new Encryption Profile.

Screenshot

screenshot showing the encryption profile form

Once saved you will be redirected to the list of your encryption profiles. Before we go further, let’s test this out.

  1. Next to your encryption profile, choose the operation “Test”.
  2. On the next page, provide some random text to the “Text to encrypt” field and click “Encrypt”. In the “Encrypted text” field you should see a long random string of characters. This is your text encrypted.
  3. Copy the encrypted text and paste it into the “Text to decrypt” field, then click “Decrypt”.
  4. Now you should see your original text in the “Decrypted text” field.

If your original text is the same as your decrypted text, then both your Key and Encryption Profile are setup correctly and ready to be used.

Screenshot

screenshot of testing the new encryption profile

Field Encrypt: Automatically encrypt field data at rest

Now that all the pieces are in place, we can start configuring our fields to encrypt their data within the database.

There are two general areas of configuration with the Field Encrypt module. First is the site-wide default settings. To view these settings navigate to Configuration > System > Field Encrypt Settings (/admin/config/system/field-encrypt).

Here you can configure which properties of a field type are checked by default when enabling field encryption. For example, if you have the Address field installed on your site and know that every time you choose to encrypt an Address field you want the the address_line1 and postal_code properties to be encrypted, you can set those here as the default.

Basically, this settings page is just to give you the administrator some common encryption defaults between field types. Changing the settings here does not affect any of your existing fields. You don’t necessarily need to modify these settings at all.

Next up, let’s configure a field to make it encrypt its data!

For the sake of example, let’s add a new field to the Basic Page content type and setup that field to be encrypted. This will be a simple text field. After we configure the field and create some data, we’ll look at the database to see how the data is stored.

  1. Navigate to Structure > Content Types > Basic Page > Manage Fields (/admin/structure/types/manage/page/fields) and click “Add Field”
  2. Field Type: choose “Text (plain)”
  3. Field Name: “Super Cool Encrypted Field”, or another name of your choosing.
  4. Click “Save and Continue”
  5. On the next page, beneath the normal Field Storage options, you should see a new checkbox labeled “Encrypt Field”. Check that box and the form will expand with some options.
  6. Properties: check “Text value” – Here we’re choosing which parts of the field data will be encrypted. Since this is a plain text field, there is only one property available to encrypt.
  7. Encryption Profile: choose “Field Encryption AES Profile” – or whatever you named the encryption profile in the previous setup.
  8. Uncacheable: Check this. This will make sure your unencrypted data will not be exposed in the cache, but will have a negative impact on your performance. Whether or not you want encrypted data to be cacheable in your actual usage of the module is up to you, but there are trade-offs in both cases. “Uncacheable” is the more secure option, but has a performance cost.
  9. Click “Save Field Settings”

Screenshot

screenshot of filed encrypt settings on a plain text field

And we’re done! You should now have a field that encrypts its data on saving to the database, and decrypts the data when shown to a site user. 🎉

Testing & Exploring the data

Let’s test this out and see what is happening behind the scenes to our data.

Create a new Basic Page and provide some text to our newly encrypted field. Everything should appear normal to you. The field data is shown decrypted when viewing or editing the node.

Screenshot: All is normal

screenshot of node form and display

But if we were to look at our new field in the database we will not find the data. Instead, the value of the field in the database is literally “[ENCRYPTED]”.

screenshot of field data in database
Note: I’m using a content type called “Test Dev” in this example, as opposed to a Basic Page.

This is because encrypted data for fields is now stored in a few new database tables provided by the field_encrypt module.

screenshot of encrypted data in database
Note: There are 2 rows shown here because I have revisions enabled for the content type.

There it is, my encrypted data.

Now to complete the circle on testing this and understanding how all these pieces fit together, I should be able to copy the encrypted data from the database, and Test Decrypt it with my Encryption Profile.

Screenshot of decrypting data taken from the database

screenshot of decrypting the data copied from the database

And viola! Works like a charm.

So there we have it, we are now encrypting field data at rest using a few modules that provide simple administrative UIs and are designed to work together.

Additional Notes and Gotcha’s

As mentioned in a previous section, when you create an encryption profile with the Encrypt module, that profile becomes available to a global encryption service within your code. Though you may never need to manage your own encryption/decryption in code, let’s take a quick look at that for reference.

Programmatically Encrypt & Decrypt Data

Assuming you have created an encryption profile named “Field Encrypt AES” (with the machine name: field_encrypt_aes), the following code is an example of using that profile programmatically.

use Drupal\encrypt\Entity\EncryptionProfile;

$string = "I bet this is going to work great!";
$encryption_profile = EncryptionProfile::load('field_encrypt_aes');

$encrypted = Drupal::service('encryption')->encrypt($string, $encryption_profile);
$decrypted = Drupal::service('encryption')->decrypt($encrypted, $encryption_profile);

dsm($encrypted);
dsm($decrypted);

Note: This example is both encrypting and decrypting right after the other. In the real world, you would do these things separately depending on the events taking place in the system.

Screenshot: Seeing is believing

screenshot of programmatic encryption using devel PHP

Note how all you need to do is load the encryption profile by machine name, and you’re ready to en/decrypt! Easy enough!

Key: Encryption keys as a service with Lockr

As mentioned in the best practices section for the Key module, the ideal secure setup is storing your key on a server that is complete separate from your Drupal application. There are likely a few good ways to do this, but of note is the service provided by Lockr.

Lockr offers keys as a service. Meaning they will generate, store, and provide API access to your encryption keys. This not only solves the problem of storing your keys securely, but it offers an additional benefit to easily scale your encryption configuration.

Consider adding encryption to an application that is load-balanced across multiple servers. If you’re encrypting data within your application, then each server will need access to the keys used for that encryption.

How to use Lockr

  1. Visit https://www.lockr.io and create a new account.
  2. Download the Lockr module and its dependencies to your Drupal instance – https://www.drupal.org/project/lockr and install it.
    • Installing with composer will automatically download the dependencies you need.
      composer require drupal/lockr
    • If you download the module another way, you will need to also download the lockr-client library to Drupal’s vendor directory – https://github.com/lockr/lockr-client
  3. After installing Lockr, visit the module’s configuration page by navigating to Configuration > System > Lockr (/admin/config/system/lockr)
  4. Here you will be guided through generating a certificate on your machine, and providing your Lockr username and password.
  5. Once the Lockr module configuration is complete visit the Key module’s configuration page at Configuration > System > Keys and click “Add Key”
  6. Key Type: select “Lockr Encryption”
  7. Key Size: select “256”
  8. Key Provider: select “Lockr”
  9. Save your new Key

Now the only thing left to do is to modify your existing Encryption Profile (or create a new one) that uses this new Lockr Key. The rest of the Field Encryption setup is the same as above.

Screenshot of Lockr Key Configuration

screenshot of key using Lockr service

 

Gotcha #1: Encrypting fields with existing data

It’s worth pointing out that at the time of this writing the field_encrypt module has a bug related to its use of cron to encrypt existing data. In testing this out I had cron hard-crash a few times and re-verified my setup to make sure I had everything configured correctly.

Luckily there is an issue for this in the queue with a working patch: https://www.drupal.org/node/2900641

Gotcha #2: Adding encryption to fields on custom entities

Also while experimenting with these modules I ran into a very confusing issue dealing with custom entities. If you add encryption to an existing field on a custom entity, you must clear your site cache before it will work correctly.

This really threw me for a loop when working with the modules, as all of my data appeared as “[ENCRYPTED]” on both entity display and the entity form. After determining there was a bug of some sort at play, I submitted an issue to the Field Encryption module: https://www.drupal.org/node/2918252

Conclusion

That’s all I have for now on data encryption at rest in Drupal 8, but I fully expect to be digging further into the modules and learning more ways to secure my site data.

If you’ve made it this far in the post let me know what you think. If you see any ways this post could be improved or have any questions about these modules, let me know and I’ll figure it out!

Update: Now in full-color video!

About the Author

Jonathan Daggerhart

Long time Drupal and WordPress developer. I like to write modules and plugins, and I dabble in frontend and design.

Leave a Reply

Your email address will not be published. Required fields are marked *