Ce qui se passe
Premier article de la série des nouveautés de Symfony 8.2, attendu fin novembre 2026 : le composant KeyManagement, contribué par Florent Morselli. Il met AWS KMS, Azure Key Vault, Google Cloud KMS et HashiCorp Vault derrière une seule API.
Le problème qu'il adresse est banal et mal outillé : chiffrer des colonnes contenant des données personnelles, des documents téléversés ou des jetons d'API, avec une clé maîtresse qui ne quitte jamais le fournisseur et dont chaque usage est audité. Jusqu'ici, chaque fournisseur imposait son SDK et son procédé.
Les paquets
| Paquet | Rôle |
|---|---|
symfony/key-management |
interfaces, chiffrement par enveloppe, backends locaux libsodium et OpenSSL |
symfony/aws-key-management |
pont AWS KMS |
symfony/azure-keyvault-key-management |
pont Azure Key Vault |
symfony/google-cloud-key-management |
pont Google Cloud KMS |
symfony/hashicorp-vault-key-management |
pont HashiCorp Vault |
symfony/doctrine-dbal-key-management |
type de colonne chiffré |
symfony/doctrine-orm-key-management |
attribut d'index aveugle |
Le composant est marqué expérimental : son API peut encore changer en version mineure.
Deux modes, et pourquoi le second gagne
Le mode direct envoie la donnée au KMS et récupère le chiffré. Simple, mais borné par la taille acceptée par le fournisseur — 4 Ko sur AWS KMS. Réservé aux petites valeurs, un jeton par exemple.
Le mode enveloppe demande au KMS une clé de données fraîche, chiffre la
donnée localement en AES-256-GCM, puis range dans l'Envelope cette clé de
données elle-même chiffrée par la clé maîtresse. La donnée ne sort jamais de
l'application, sa taille est libre, et la même enveloppe se déchiffre à
l'identique quel que soit le backend qui l'a produite — tant que la clé maîtresse
reste joignable. Votre code ne manipule jamais que l'identifiant de cette clé :
un alias, un ARN, une URL.
Côté configuration, on déclare un ou plusieurs clients par DSN sous la clé
key_management, puis on injecte EnvelopeEncrypterInterface (ou
EncrypterInterface / DecrypterInterface pour le mode direct), avec
l'attribut #[Target] pour désigner un client autre que celui par défaut. Le
composant ajoute quatre commandes console — dont deux qui lisent l'entrée
standard et écrivent sur la sortie, donc chaînables pour réencapsuler une
enveloppe sous une autre clé — et un panneau dans la barre de débogage en
environnement dev.
Le vrai problème : retrouver une ligne chiffrée
Le chiffrement est randomisé : deux lignes portant la même valeur produisent deux
chiffrés différents, et WHERE email = ? ne correspond donc jamais à rien.
C'est à quoi sert l'attribut #[BlindIndexed] du pont ORM. Une colonne d'index
stocke un condensé à clé de la valeur, que l'ORM remplit automatiquement à chaque
flush. La recherche porte sur cet index, pas sur le chiffré. L'index est un
service, qui a besoin d'un client KMS et d'une clé encapsulée créée par
key-management:generate-data-key.
Le pont DBAL fournit de son côté EncryptedType, qui décore n'importe quel type
Doctrine existant. Le nom du type est le vôtre : on liste les noms dans un
service EncryptedTypes — chacun associé au type Doctrine qu'il enveloppe et à
la clé sous laquelle il chiffre — et on appelle son register() depuis le
boot() du kernel, pour que les types existent avant la première requête.
Le détail qui fait la différence à l'échelle
L'option key_management.store range les clés de données dans une table. Chaque
ligne chiffrée ne porte plus alors qu'une référence de 16 octets au lieu d'une
clé encapsulée complète, et le KMS n'est appelé qu'une fois par clé de données,
non par ligne.
C'est ce qui rend possible la commande key-management:rewrap-data-keys :
changer de clé maîtresse, ou même de fournisseur, réencapsule les clés stockées
sans lire ni réécrire une seule ligne chiffrée.
À retenir
Le composant ne prétend pas rendre le chiffrement transparent — il rend transparent le changement de fournisseur, et fournit la pièce manquante des implémentations maison : une colonne chiffrée qu'on peut encore interroger. Statut expérimental oblige, on l'essaie sur une colonne, pas sur toute la base.