Nouveau dans Symfony 8.2 (en développement). Un téléphone, une date de naissance, un numéro de sécurité sociale : certaines colonnes ne devraient pas se lire en clair dans une sauvegarde qui traîne ou dans une base copiée sur un poste de développement. Les chiffrer demandait jusqu'ici un bundle tiers, ou un type Doctrine écrit à la main. Symfony 8.2 apporte le composant KeyManagement, et son pont Doctrine chiffre une colonne avec un attribut.
Avant
final class TelephoneChiffreType extends Type { public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { // une clé à charger, un IV à tirer, openssl_encrypt(), le tag à ranger… } public function convertToPHPValue($value, AbstractPlatform $platform): ?string { // … et tout l'inverse } }
Puis un type de ce genre par type de valeur (texte, date…), à déclarer dans doctrine.yaml.
Depuis Symfony 8.2
use Symfony\Component\KeyManagement\Bridge\DoctrineOrm\Attribute\Encrypted; #[ORM\Column(nullable: true)] #[Encrypted(key: 'patient.contact')] private ?string $telephone = null;
Doctrine écrit dans la base une valeur chiffrée et binaire, puis la déchiffre en relisant l'entité. La propriété garde son type : un string reste un string, et une date reste un DateTimeImmutable, car le type de la colonne convertit d'abord la valeur, qui est ensuite chiffrée. Il n'y a ni type à déclarer ni écouteur à écrire : en compilant le conteneur, Symfony trouve les propriétés marquées dans src/Entity/ et déclare les types Doctrine qu'il leur faut.
Le chiffrement est aléatoire : la même valeur, chiffrée deux fois, ne donne jamais les mêmes octets. C'est ce qui empêche de deviner qui partage un numéro, mais c'est aussi pourquoi une colonne chiffrée ne se cherche plus avec un WHERE et ne se trie plus avec un ORDER BY. On ne chiffre donc que ce qui doit l'être.
Le composant pratique le chiffrement d'enveloppe : chaque valeur est chiffrée par une clé de données, elle-même chiffrée par une clé maîtresse qui ne quitte pas le KMS (AWS KMS, Google Cloud KMS, Vault…). Dans cet environnement, le KMS est local (OpenSSL), et les clés de données sont rangées en base (key_management.store). Le paramètre key nomme alors une portée : chaque portée a sa propre clé de données.
À faire
Le cabinet garde ses patients dans l'entité Patient. L'aperçu /patients montre ce que l'application lit et, sous enBase, ce que la base contient vraiment : pour l'instant, tout est en clair.
- Chiffrez le téléphone sous la clé
patient.contact. - Chiffrez la date de naissance sous la clé
patient.identite. - Laissez le nom en clair : la liste est triée par nom, en SQL.
L'application doit lire exactement les mêmes patients qu'avant.