Aller au contenu principal
Stéphane Quantin

Main navigation

  • Portfolio
  • À Propos
  • Freelance Drupal
    • Audit de site Drupal
    • Contrat de maintenance Drupal
  • Blog
  • CV
  • Contact
Languages
  • French
  • English

Fil d'Ariane

  1. Accueil
  2. Blog

SA-CORE-2026-004 : faille SQL Injection hautement critique dans Drupal Core

Par StephaneQ , jeu, 05/21/2026 - 16:39

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.10

Root 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\Condition

Cette 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
:field2

La 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 status

Il est également utile de vérifier la version installée via Composer :

composer show drupal/core-recommended
composer show drupal/core

Si 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-dependencies

Exemple 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-dependencies

Exemple 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-dependencies

Après la mise à jour Composer, appliquer le processus Drupal habituel :

drush updatedb
drush cache:rebuild

Puis vérifier l’état du site :

drush status
composer audit

Plan d’intervention conseillé

  1. Identifier la version Drupal actuelle : Drupal 10.4, 10.5, 10.6, 11.2, 11.3, etc.
  2. Identifier le moteur de base de données : PostgreSQL, MySQL, MariaDB ou autre.
  3. Sauvegarder le site : fichiers, base de données, configuration exportée et fichier composer.lock.
  4. Mettre à jour Drupal Core vers la version corrigée correspondant à la branche utilisée.
  5. Exécuter les updates Drupal avec drush updatedb.
  6. Reconstruire les caches avec drush cache:rebuild.
  7. Tester les parcours critiques : authentification, formulaires, back-office, Views, recherche, API, tunnel e-commerce le cas échéant.
  8. Contrôler les rôles utilisateurs sensibles, notamment ceux qui peuvent modifier des templates Twig ou des vues.
  9. 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é.

Sources

  • Drupal core - Highly critical - SQL injection - SA-CORE-2026-004
  • Upcoming highly critical release on May 20, 2026 - PSA-2026-05-18
  • Release notes Drupal 10.6.9
  • Release notes Drupal 11.3.10
  • Twig 3.26.0 released

Étiquettes

  • Drupal
  • Drupal 11
  • Drupal 10
  • Drupal 9

Réseaux sociaux

  • Malt
  • codeur.com
  • 404Works
  • LinkedIn
  • Twitter
  • DoYouBuzz

Twitter

Tweets by @StephaneQ
RSS feed

Pied de page

  • Contact
  • Mentions légales
Propulsé par Drupal