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
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
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.
I have been waiting for this feature for years.