The new KeyManagement component includes a Doctrine DBAL bridge with an EncryptedType that encrypts any column with envelope encryption. You need a separate Doctrine type for each combination of key and value type, registered before the first query runs. Instead of making you write that setup code, Symfony 8.2 provides two ways to create and register those types for you.

Encrypting Entity Properties with an Attribute

Nicolas Grekas
Contributed by Nicolas Grekas in #66806

If you use the Doctrine ORM, add the new #[Encrypted] attribute to any mapped property to encrypt its column. You don't need to declare any custom Doctrine type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\KeyManagement\Bridge\DoctrineOrm\Attribute\Encrypted;

#[ORM\Entity]
class Customer
{
    // ...

    #[ORM\Column(nullable: true)]
    #[Encrypted(key: 'customer.contact')]
    private ?string $phone = null;

    #[ORM\Column(nullable: true)]
    #[Encrypted(key: 'customer.identity')]
    private ?\DateTimeImmutable $birthDate = null;
}

Doctrine handles these properties as usual ($phone is a string and $birthDate a date) but, before saving them, Symfony encrypts their values and stores the result as binary data in the database. When loading the entity, the values are decrypted automatically, so your code always works with the original string and DateTimeImmutable values.

The key argument tells which key encrypts the values. It's the identifier of a master key in your KMS or, if you store data keys in the database (the key_management.store option), a name that groups the values encrypted with the same data key.

You don't need to configure anything else. When compiling the container, Symfony finds the #[Encrypted] attributes in your entities and creates the Doctrine types they need. It looks for entities in the same directories where it looks for your services, which includes src/Entity/ in the default configuration. If some entity lives elsewhere or is mapped with XML, Symfony can't find its attributes and you'll see a clear error message. In that case, declare its types in the configuration, as explained in the next section.

Declaring Encrypted Types in the Configuration

Nicolas Grekas
Contributed by Nicolas Grekas in #66766

If you use Doctrine DBAL without the ORM, or if the #[Encrypted] attribute cannot find some entity, use the new doctrine_dbal.types option to declare the encrypted types. The bundle registers them when the kernel boots:

1
2
3
4
5
6
7
8
9
10
11
12
# config/packages/key_management.yaml
key_management:
    clients:
        default: '%env(KMS_DSN)%'
    store:
        key_id: 'alias/app-key'
    doctrine_dbal:
        types:
            # the 'type' option is the Doctrine type that converts the value
            # before encrypting it; 'key' works as in the #[Encrypted] attribute
            encrypted_phone: { type: string, key: customer.contact }
            encrypted_notes: { type: text, key: customer.notes }

These types use the store-backed envelope encrypter when key_management.store is configured and the encrypter of the default client otherwise. Columns with an #[Encrypted] attribute reuse the type declared here for the same key and type instead of registering a new one.

Published in #Living on the edge