Storing Secrets in Craft CMS

July 6, 2021
by Ben Croker

The most secure way of preventing secrets from being revealed is, well, not storing them at all. But assuming you need to access sensitive secrets in your PHP code, such as credentials to an online bank account, then storing them encrypted is much more secure than in plaintext.

Secrets

Here at PutYourLightsOn, we’re often building custom plugins for clients that need to connect to third-party APIs. But the conventional approach of storing secret API keys and other credentials in the .env file has never sat right with us. Even though the .env file is located outside of the public web folder, anyone accidentally gaining read access to it is able to see the secrets in plaintext. Additionally, environment variables can leak into error logs and may in some instances not be obfuscated.

We explored various approaches to avoiding this including third-party secret managers such as AWS Secrets Manager, Google Secret Manager and HashiCorp Vault. While these types of services have their advantages (access permissions, secrets rotation, cloud storage), they are often overkill for what we really wanted – a simple, secure and server agnostic way of storing and managing secrets.

Secrets Plugin #

In the end we opted to build our own Secrets plugin for Craft, which has zero dependencies, is easy to use and is free. Secrets can be added to an encrypted file and managed using console commands. 

Secrets CLI

The encrypted secrets file is stored at /config/secrets.enc by default, but this is configurable. There are some benefits to storing secrets this way.

  1. Secrets are encrypted and cannot be revealed without the encryption key.
  2. The encrypted file can be committed to your version control repository, meaning you avoid having to send secrets in plaintext to other developers. You also end up with a history of changes to the file.
  3. If secrets change or are rotated then there is only one file that needs to be updated. 

The values of secrets can be fetched using PHP code.

use putyourlightson\secrets\Secrets;

// Returns the value of the `apiKey` secret.
Secrets::getValue('apiKey');

Considerations #

The secrets file is encrypted using Craft’s security key by default, although this is configurable. It is important to consider that whatever encryption key is used must be available to PHP in plaintext. There is no getting around this.

Where you decide to store the encryption key, however, is up to you. By default, Craft’s security key is stored in the .env file, but you can move this to a server configuration file if that feels more secure to you. The important thing is that the Secrets plugin will be unaffected by this change.

Another important consideration is performance. While environment variables stored in the .env file are available to PHP on every request, encrypted secrets are not. Decryption is not a particularly fast process, and so the Secrets plugin should ideally only be used to store API keys and credentials that are not required on every request.