Skip to content

The publication for web craftspeople Wednesday, 16 September 2026

Back-end

Symfony 8.2 adds KeyManagement, one API to encrypt data behind any KMS

Unveiled on 15 September 2026, the KeyManagement component gives Symfony 8.2 a single API to encrypt sensitive data behind AWS KMS, Azure Key Vault, Google Cloud KMS, HashiCorp Vault or a local backend. Still experimental, it keeps the master key out of…

Symfony 8.2 composant KeyManagement

On 15 September 2026 Symfony introduced KeyManagement, a component that encrypts an application’s sensitive data without the code ever touching the master key. Contributed by Florent Morselli, it targets Symfony 8.2, due at the end of November 2026. It ships as experimental: its API may change between minor versions.

Keeping the master key out of the application

The promise is simple: the application only ever handles key identifiers, never the cryptographic secret itself. Encryption and decryption are delegated to a key management system (KMS), whether hosted by a cloud provider or run locally for development. The core package symfony/key-management provides the interfaces, envelope encryption and local backends; each remote provider is added through a dedicated bridge package.

This separation reshapes the blast radius of an incident. An exfiltrated database yields nothing but inert envelopes until an attacker also reaches the KMS, extending to the data layer the same hardening discipline already applied to servers and access.

ProviderBridge packageTypical use
AWS KMSsymfony/aws-key-managementAWS cloud production
Azure Key Vaultsymfony/azure-keyvault-key-managementAzure production
Google Cloud KMSsymfony/google-cloud-key-managementGCP production
HashiCorp Vaultsymfony/hashicorp-vault-key-managementSelf-managed infrastructure
Local (libsodium / OpenSSL)symfony/key-managementDevelopment and testing

Two modes: direct for small secrets, envelope for everything else

Direct mode sends the payload to the KMS, which returns the ciphertext. It suits short values such as an API token and stays bound by provider limits, around 4 KB on AWS KMS. Envelope mode removes that ceiling: the data is encrypted locally with AES-256-GCM using a data key generated on the fly, and only that data key is handed to the KMS. Content size becomes irrelevant.

use Symfony\Component\KeyManagement\Envelope;
use Symfony\Component\KeyManagement\EnvelopeEncrypter;
use Symfony\Component\KeyManagement\KeyLoader\InMemoryKeyLoader;
use Symfony\Component\KeyManagement\Local\SodiumKms;

$kms = new SodiumKms(new InMemoryKeyLoader([
    'app-key' => sodium_crypto_aead_xchacha20poly1305_ietf_keygen(),
]));

$ciphertext = $kms->encrypt('app-key', $apiToken);
$apiToken   = $kms->decrypt($ciphertext);

$encrypter = new EnvelopeEncrypter($kms);
$envelope  = $encrypter->encrypt('app-key', $fileContents);
file_put_contents($path, $envelope);

$fileContents = $encrypter->decrypt(Envelope::fromBytes(file_get_contents($path)));

Envelopes are provider-agnostic: data encrypted with one backend decrypts with another as long as the key is reachable. That portability, paired with an optional data-key table, enables row-by-row rewrap, re-encrypting under a new key without ever exposing the plaintext.

An exfiltrated database yields nothing but inert envelopes as long as the KMS stays out of reach.

Configuration, injection and Doctrine integration

On the framework side, KMS clients are declared by DSN and swapped for a sodium:// backend in development, with no cloud dependency.

# config/packages/key_management.yaml
key_management:
    clients:
        aws: '%env(AWS_KMS_DSN)%'
        vault: '%env(VAULT_KMS_DSN)%'
    default_client: aws

when@dev:
    key_management:
        clients:
            aws: 'sodium://?keys[app-key]=%env(DEV_KMS_KEY)%'
            vault: 'sodium://?keys[app-key]=%env(DEV_KMS_KEY)%'

Dependency injection follows the usual conventions, with the #[Target] attribute selecting a specific client. Two Doctrine bridges round it out: an encrypted column type and a #[BlindIndexed] attribute that, through a blind index, makes an encrypted column searchable despite the randomised ciphertext.

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\KeyManagement\BlindIndex\Email;
use Symfony\Component\KeyManagement\Bridge\DoctrineOrm\Attribute\BlindIndexed;

#[ORM\Entity]
class User
{
    #[ORM\Column(type: 'encrypted_string')]
    private string $email = '';

    #[ORM\Column(length: 64)]
    #[BlindIndexed('email', Email::class)]
    private string $emailIndex = '';
}

Console commands support day-to-day operations: key-management:encrypt, key-management:decrypt, key-management:generate-data-key and key-management:rewrap-data-keys. A dedicated panel in the debug toolbar lists KMS calls in the development environment, in the same spirit of traceability that infrastructure monitoring brings to the back-end stack.

Experimental status. The API may change in a minor version. A project adopting it now should wrap its calls behind an application layer and track release notes, rather than scattering the component’s classes across the domain code.

Key takeaways

KeyManagement standardises in Symfony a practice that used to be hand-rolled project by project: application-level encryption backed by a KMS. Envelope mode, envelope portability and the blind index address the three recurring obstacles of data size, provider lock-in and searching encrypted columns. The experimental status warrants caution in production, but the direction is clear and the Doctrine integration sharply lowers the cost of entry.

I have seen too many databases where personal data sat in plaintext “because we’ll get to encryption later”. What I like here is that the blind index settles the one technical argument that still held: being able to search an encrypted column. I would still keep the scope tight until the API stabilises, then widen it once 8.2 ships. — Simon Janvier

Further reading: the official component announcement on the Symfony blog.

Read next