Le 20 mai 2026, l’équipe sécurité Drupal a publié l’avis de sécurité SA-CORE-2026-004, classé Highly Critical avec un score de risque Drupal de 20/25.
Cette faille, référencée CVE-2026-9082, concerne une vulnérabilité de type SQL Injection dans Drupal Core. Elle touche plus précisément la couche d’abstraction de base de données de Drupal, dans un cas lié aux requêtes Entity Query exécutées sur des bases PostgreSQL.
Drupal avait publié une alerte préventive le 18 mai 2026 via PSA-2026-05-18, afin d’inviter les mainteneurs de sites à réserver un créneau de mise à jour dès la publication du correctif.
Résumé de la faille
La vulnérabilité permet à un attaquant d’envoyer des requêtes spécialement construites pouvant mener à une injection SQL arbitraire sur les sites Drupal utilisant PostgreSQL.
L’exploitation peut être réalisée par des utilisateurs anonymes, sans authentification préalable. Les impacts potentiels mentionnés par Drupal incluent la divulgation d’informations, l’élévation de privilèges et, dans certains cas, l’exécution de code à distance ou d’autres attaques en chaîne.
Point important : la faille SQL Injection elle-même concerne uniquement les sites utilisant PostgreSQL. Cependant, les releases publiées embarquent également des mises à jour de sécurité de dépendances tierces, notamment Twig, Symfony et Composer. Il est donc recommandé de mettre à jour tous les sites Drupal concernés, même lorsqu’ils utilisent MySQL ou MariaDB.
Versions Drupal impactées
Les versions vulnérables indiquées par Drupal sont les suivantes :
| Branche Drupal | Versions impactées | Version corrigée recommandée |
|---|---|---|
| Drupal 11.3.x | < 11.3.10 | 11.3.10 |
| Drupal 11.2.x | < 11.2.12 | 11.2.12 |
| Drupal 11.1.x / 11.0.x | < 11.1.10 | 11.1.10 |
| Drupal 10.6.x | < 10.6.9 | 10.6.9 |
| Drupal 10.5.x | < 10.5.10 | 10.5.10 |
| Drupal 10.4.x et versions antérieures | < 10.4.10 | 10.4.10 |
| Drupal 9.x | Fin de vie | Patch manuel best effort |
| Drupal 8.9.x | Fin de vie | Patch manuel best effort |
| Drupal 7 | Non affecté | Aucune action spécifique pour cette faille |
Drupal liste précisément les versions affectées sous la forme suivante :
>= 8.9.0 < 10.4.10
|| >= 10.5.0 < 10.5.10
|| >= 10.6.0 < 10.6.9
|| >= 11.0.0 < 11.1.10
|| >= 11.2.0 < 11.2.12
|| >= 11.3.0 < 11.3.10Root cause : que s’est-il passé côté code ?
La cause racine se situe dans le traitement de certaines conditions SQL générées par les Entity Queries, plus précisément dans l’implémentation PostgreSQL.
Drupal dispose d’une Database Abstraction API dont le rôle est de construire des requêtes SQL portables et sécurisées entre plusieurs moteurs de base de données. En principe, les valeurs dynamiques doivent être passées via des placeholders et non concaténées directement dans la requête.
Dans le cas vulnérable, le problème ne venait pas d’une valeur SQL injectée directement, mais de la construction du nom du placeholder lui-même.
Le code PostgreSQL concerné traitait certains tableaux de valeurs sur des champs non sensibles à la casse. Schématiquement, la logique ressemblait à ceci :
$where_prefix = str_replace('.', '_', $condition['real_field']);
foreach ($condition['value'] as $key => $value) {
$where_id = $where_prefix . $key;
$condition['where'] .= 'LOWER(:' . $where_id . '),';
$condition['where_args'][':' . $where_id] = $value;
}Les valeurs étaient bien passées dans $condition['where_args']. Elles étaient donc paramétrées. En revanche, la clé du tableau, ici $key, était utilisée pour construire dynamiquement le nom du placeholder :
$where_id = $where_prefix . $key;Puis ce placeholder était injecté dans la chaîne SQL :
$condition['where'] .= 'LOWER(:' . $where_id . '),';En PHP, un tableau peut être indexé numériquement :
[
'foo',
'bar',
]Mais il peut aussi être associatif :
[
'cle_1' => 'foo',
'cle_2' => 'bar',
]Le problème vient du fait que le code utilisait les clés du tableau pour fabriquer les noms des placeholders SQL. Si ces clés pouvaient être influencées indirectement par une entrée utilisateur, elles pouvaient se retrouver dans le fragment SQL généré.
Autrement dit, Drupal protégeait correctement les valeurs, mais une donnée potentiellement non fiable pouvait encore influencer la structure de la requête, via le nom du placeholder.
Pourquoi PostgreSQL uniquement ?
La faille est liée à une branche spécifique du code utilisée pour PostgreSQL, notamment dans la classe :
Drupal\Core\Entity\Query\Sql\pgsql\ConditionCette logique est exécutée dans un cas particulier : lorsque la condition porte sur un tableau de valeurs et que le champ est traité comme non sensible à la casse.
Dans ce cas, Drupal construit une clause de type :
LOWER(champ) OPERATOR (LOWER(:placeholder0), LOWER(:placeholder1), ...)C’est dans cette construction spécifique que le nom des placeholders pouvait être influencé par les clés du tableau fourni à la condition.
Cela explique pourquoi l’advisory officiel précise que la vulnérabilité SQL Injection ne concerne que les sites utilisant PostgreSQL.
Le correctif
Le principe du correctif est simple : ne plus réutiliser les clés associatives du tableau dans la génération des placeholders SQL.
La correction consiste à forcer une réindexation numérique des valeurs, par exemple avec array_values(), avant de parcourir le tableau :
foreach (array_values($condition['value']) as $key => $value) {
$where_id = $where_prefix . $key;
$condition['where'] .= 'LOWER(:' . $where_id . '),';
$condition['where_args'][':' . $where_id] = $value;
}Avec cette approche, même si le tableau initial est associatif, ses clés sont ignorées. Après réindexation, les clés deviennent nécessairement numériques : 0, 1, 2, etc.
Les noms de placeholders redeviennent donc entièrement contrôlés par Drupal, par exemple :
:field0
:field1
:field2La faille vient donc d’un détail subtil : l’utilisation d’une donnée non fiable non pas comme valeur SQL, mais comme élément de construction d’un placeholder SQL.
Un exemple de mauvaise hypothèse de sécurité
Cette faille illustre une erreur fréquente dans les couches d’abstraction SQL : considérer que l’usage de placeholders suffit à sécuriser toute la requête.
Les placeholders protègent les valeurs liées à la requête. Ils ne protègent pas automatiquement les éléments qui servent à construire la structure SQL elle-même : noms de champs, opérateurs, fragments SQL ou, dans ce cas précis, noms de placeholders générés dynamiquement.
La règle à retenir est la suivante : toute donnée pouvant influencer la structure d’une requête SQL doit être considérée comme sensible, même lorsqu’elle ne correspond pas directement à une valeur métier.
Mises à jour de dépendances incluses
Les releases publiées pour Drupal 10 et Drupal 11 ne corrigent pas uniquement cette SQL Injection.
Elles intègrent aussi des mises à jour de sécurité pour plusieurs dépendances :
- Twig 3.26.0, qui corrige plusieurs vulnérabilités de sécurité ;
- Symfony, mis à jour en 6.4.40 pour Drupal 10 et 7.4.12 pour Drupal 11 ;
- Composer 2.9.8, inclus comme mesure de durcissement ;
- underscore.js 1.13.8 pour Drupal 10.6.9.
Drupal recommande également de vérifier quels rôles utilisateurs peuvent modifier des templates Twig, par exemple via Views ou via certains modules contribués.
Actions recommandées
La première action consiste à identifier la version exacte de Drupal Core et le moteur de base de données utilisé par le site.
drush statusIl est également utile de vérifier la version installée via Composer :
composer show drupal/core-recommended
composer show drupal/coreSi le site utilise PostgreSQL, la mise à jour doit être considérée comme prioritaire. Si le site utilise MySQL ou MariaDB, la faille SQL Injection ne s’applique pas directement, mais la mise à jour reste recommandée en raison des correctifs Twig, Symfony et autres dépendances.
Commande générique Composer
Pour mettre à jour Drupal Core et ses dépendances associées :
composer update "drupal/core-*" --with-all-dependenciesExemple pour Drupal 10.6.9
composer require drupal/core-recommended:10.6.9 drupal/core-composer-scaffold:10.6.9 drupal/core-project-message:10.6.9 --update-with-all-dependenciesExemple pour Drupal 11.3.10
composer require drupal/core-recommended:11.3.10 drupal/core-composer-scaffold:11.3.10 drupal/core-project-message:11.3.10 --update-with-all-dependenciesAprès la mise à jour Composer, appliquer le processus Drupal habituel :
drush updatedb
drush cache:rebuildPuis vérifier l’état du site :
drush status
composer auditPlan d’intervention conseillé
- Identifier la version Drupal actuelle : Drupal 10.4, 10.5, 10.6, 11.2, 11.3, etc.
- Identifier le moteur de base de données : PostgreSQL, MySQL, MariaDB ou autre.
- Sauvegarder le site : fichiers, base de données, configuration exportée et fichier
composer.lock. - Mettre à jour Drupal Core vers la version corrigée correspondant à la branche utilisée.
- Exécuter les updates Drupal avec
drush updatedb. - Reconstruire les caches avec
drush cache:rebuild. - Tester les parcours critiques : authentification, formulaires, back-office, Views, recherche, API, tunnel e-commerce le cas échéant.
- Contrôler les rôles utilisateurs sensibles, notamment ceux qui peuvent modifier des templates Twig ou des vues.
- Surveiller les logs applicatifs, web serveur et base de données après déploiement.
Cas des sites Drupal 8 et Drupal 9
Drupal 8 et Drupal 9 sont en fin de vie. Aucune release complète n’est publiée pour ces branches.
Compte tenu de la sévérité de la faille, Drupal fournit toutefois des patchs manuels pour Drupal 8.9 et Drupal 9.5. Ces patchs sont fournis en best effort : ils peuvent aider à réduire le risque à court terme, mais ils ne constituent pas une solution durable.
Ces anciennes versions conservent d’autres vulnérabilités connues qui ne seront pas corrigées par ces patchs. La recommandation reste donc de planifier rapidement une migration vers une branche supportée, idéalement Drupal 10.6 ou Drupal 11.3 selon le contexte du projet.
Faut-il mettre à jour si le site n’utilise pas PostgreSQL ?
Oui, la mise à jour reste recommandée.
La SQL Injection décrite dans SA-CORE-2026-004 concerne PostgreSQL uniquement. En revanche, les releases Drupal publiées incluent aussi des correctifs de sécurité pour Twig, Symfony et d’autres dépendances.
Un site utilisant MySQL ou MariaDB n’est donc pas exposé à cette SQL Injection précise, mais peut être concerné par les vulnérabilités corrigées dans les dépendances embarquées.
Conclusion
SA-CORE-2026-004 est une faille importante à traiter rapidement, en particulier pour les sites Drupal utilisant PostgreSQL.
La cause racine est intéressante d’un point de vue technique : Drupal paramétrait correctement les valeurs SQL, mais utilisait encore les clés d’un tableau pour construire dynamiquement les noms de placeholders SQL dans certains cas d’Entity Query PostgreSQL.
Le correctif consiste à ne plus faire confiance aux clés du tableau et à ne conserver que les valeurs, en forçant une réindexation numérique avant la génération des placeholders.
Au-delà de cette faille, cette publication rappelle l’importance d’avoir une procédure de maintenance Drupal claire : suivi des advisories, environnement de préproduction, sauvegardes fiables, tests de non-régression et capacité à déployer rapidement les mises à jour de sécurité.