Le blog

FrankenPHP 1.13.5 et Caddy 2.11.9 : des versions renforcées grâce à l’OSS Scanner d’Anthropic

Publié le 12 novembre 2026

FrankenPHP 1.13.5 et Caddy 2.11.9 viennent de sortir. Avec Caddy 2.11.8, publié juste avant, ces versions corrigent une trentaine de vulnérabilités, dont beaucoup ont été trouvées par l’OSS Scanner d’Anthropic, et ils rendent les deux serveurs plus rapides. Si vous servez du PHP avec FrankenPHP ou Caddy, mettez à jour dès maintenant ! FrankenPHP 1.13.5 ferme à lui seul 17 advisories de sécurité. La plupart des correctifs de Caddy touchent son transport FastCGI, le code qui appelle PHP-FPM depuis Caddy, et nous en avons écrit une bonne partie. Voici ce qui a été corrigé, et la gravité réelle de ces failles.

Cet article a d’abord été publié en anglais sur le blog de Tilia, l’offre IA et infra de notre coopérative.

Digital code on a black computer screen

Une infrastructure critique, analysée par l’IA

Anthropic a lancé l’OSS Scanner en octobre 2026. C’est un service gratuit, sur inscription, qui passe le code open source au crible des meilleurs modèles d’Anthropic et envoie les résultats aux mainteneurs, après relecture humaine, dans le cadre d’une divulgation coordonnée. Il n’accepte pas n’importe quel projet : il retient les projets établis qui ont un impact critique sur l’infrastructure et la sécurité des utilisateurs.

FrankenPHP remplit ces critères. Il fait tourner en production des applications Laravel, Symfony, WordPress et Drupal, y compris sur des plateformes comme Laravel Cloud, Upsun et Clever Cloud. Une faille dans FrankenPHP, c’est une faille dans des milliers de sites. Et comme FrankenPHP repose sur Caddy, l’analyser revient aussi à analyser le code de Caddy qu’il utilise.

Les rapports étaient bons, et ils étaient nombreux. Chacun doit être reproduit, corrigé, testé, rétroporté et publié par un mainteneur. Les scanners IA signalent des bugs plus vite que jamais, et le travail côté mainteneurs, lui, n’a pas diminué. C’est pour ça que nous sponsorisons ces projets, et vous devriez le faire aussi.

Corrigé dans Caddy, pour tout le monde

À la base, FrankenPHP est une bibliothèque Go, intégrée à un module Caddy et désormais maintenue au sein de l’organisation PHP sur GitHub. Quand une analyse de FrankenPHP révèle un bug, la cause se trouve donc souvent dans Caddy lui-même, et c’est là qu’on le corrige. Tout le monde en profite : les utilisateurs de FrankenPHP, ceux qui font tourner PHP-FPM derrière le php_fastcgi de Caddy et, pour les correctifs du cœur de Caddy, tous les sites servis par Caddy, en PHP ou non.

Dans Caddy 2.11.8 :

  • Path traversal via l’en-tête Host (GHSA-r766-rf9f-588h). Une valeur Host ou :authority contenant des segments point ou des slashs pouvait sortir du répertoire prévu quand elle était utilisée dans un chemin de fichier, comme root /srv/sites/{host}, et même, avec php_fastcgi, exécuter des fichiers situés en dehors. Ces hôtes sont maintenant rejetés avec une 400, sur tous les protocoles.
  • Contournement des path matchers en FastCGI (GHSA-f278-mgc7-46j3). SCRIPT_FILENAME est maintenant calculé à partir du même chemin normalisé que celui que voient les path matchers. /admin.php/../../other.php ne peut donc plus exécuter un script qu’une règle de chemin devait protéger.
  • Contournement du file matcher (GHSA-25cw-38mq-q7m6). Un chemin comme /.env/./ pointait vers le fichier et passait à travers des règles comme path *.env.
  • Path info doublement encodé (GHSA-69hv-5jfc-mvmv). Le reste capturé par le file matcher est maintenant échappé avant la réécriture : ..%252f ne peut plus devenir ../ une fois les règles de chemin passées.
  • Répertoire de données exposé (GHSA-jf74-f69g-hcj9). Sans HOME ni XDG_DATA_HOME, Caddy stockait ses certificats et ses clés privées dans ./caddy, qu’un file_server servant le répertoire de travail pouvait exposer. Ce répertoire est maintenant toujours masqué.
  • Open redirect dans la redirection avec slash final de php_fastcgi (#8285), et en-têtes hop-by-hop retirés des réponses 1xx, ce qui complète le correctif de la CVE-2026-55458.

Dans Caddy 2.11.9, un correctif rapide par-dessus :

  • env FastCGI écrasé par le client (GHSA-q34h-28rj-5hjm, #8358). Un client pouvait écraser n’importe quelle variable HTTP_* définie avec la sous-directive env en envoyant l’en-tête correspondant, et par exemple choisir l’utilisateur qu’une application lit dans HTTP_X_AUTH_USER. Les valeurs configurées l’emportent désormais.
  • Usurpation du Host en HTTP/2 (#8355). Un champ host envoyé à côté de :authority pouvait faire voir à l’application un HTTP_HOST différent de l’hôte vers lequel Caddy avait routé la requête.
  • Chemins de script ambigus (#8357, #8356). Les slashs encodés et les segments point, et sous Windows les noms courts NTFS 8.3 et les flux de données alternatifs, pouvaient faire approuver un script par une règle de chemin pendant qu’un autre s’exécutait. Ces requêtes reçoivent maintenant une 400.
  • mTLS affaibli sans prévenir (#8306). Quand deux blocs de site définissaient un client_auth différent pour le même hôte, Caddy gardait le premier et ignorait l’autre, parfois le plus strict. C’est maintenant une erreur dans le Caddyfile.

La 2.11.9 corrige aussi une régression de la 2.11.8 qui cassait les index de répertoire comme /wp-admin/. Si vous faites tourner WordPress en 2.11.8, celle-ci est pour vous.

Corrigé dans FrankenPHP 1.13.5

FrankenPHP 1.13.5 corrige 17 vulnérabilités et embarque Caddy 2.11.9 et Mercure 1.1.6 : il apporte donc aussi tous les correctifs ci-dessus. La seule classée haute :

  • Fichiers non PHP exécutés comme du PHP (GHSA-j99q-3wr8-xj43). php_server pouvait exécuter un fichier dont le nom ne se termine pas par une extension split, par exemple /uploads/photos.php.php/avatar.jpg. Un attaquant capable de déposer un fichier sous le document root pouvait l’exécuter.

Celles qui touchent la configuration par défaut de php_server, toutes classées moyennes :

  • Open redirect. La redirection avec slash final pour les répertoires reprenait le chemin brut de la requête : //attacker.example/..%2fblog envoyait donc les visiteurs vers un autre hôte. La redirection est maintenant relative, comme dans php_fastcgi (GHSA-jvmv-5538-pqpg).
  • Usurpation du Host en HTTP/2. Même problème que dans Caddy, côté FrankenPHP, qui remplissait $_SERVER['HTTP_HOST'] (GHSA-2725-rh2w-9j9w).
  • Corps de requête lents. La directive request_body_timeout ne les arrêtait pas : une connexion lente par thread suffisait à bloquer tous les threads PHP. Corrigé par Caddy 2.11.8 (GHSA-j45x-h238-h4xx).
  • Plantages. Une requête avec plus de paramètres que max_input_vars, avec les réglages PHP des images Docker officielles, pouvait finir en segmentation fault (GHSA-5359-vg53-5464), et die(-1) faisait paniquer FrankenPHP (GHSA-q6vv-3p75-53wr).

Les autres demandent une configuration particulière :

  • Règles de chemin Caddy contournées avec des segments point et des slashs encodés après un script, comme /admin/tool.php/../../missing.php (GHSA-2x4j-r638-wxff).
  • Valeurs env écrasées. L’en-tête Authorization: Basic d’un client remplaçait un REMOTE_USER défini avec env, de même que quatre autres variables, depuis la 1.12.0 (GHSA-388m-835f-7723).
  • Named routes. Elles pouvaient s’exécuter avec le root, les valeurs env et les workers d’un autre site, depuis la 1.13.0 (GHSA-g7pw-whv8-97j7).
  • Divulgation de code source avec une règle match de worker et une extension split supplémentaire, ou certaines réécritures (GHSA-v7hf-4gqv-hf8p).
  • Mode worker. Les destructeurs d’une requête précédente, y compris ceux des objets de session, pouvaient envoyer de la sortie et des cookies au client suivant (GHSA-8qcf-v46r-g2mc).
  • Applications embarquées. Le répertoire extrait était supprimé au rechargement alors qu’il était encore servi : un autre utilisateur local pouvait le recréer avec son propre code, depuis la 1.13.2 (GHSA-j2f3-wxf2-8qgv).
  • Cinq failles de faible gravité : le Caddyfile servi depuis le document root avec l’arborescence minimale documentée, une panique quand l’OS refuse un nouveau thread pendant l’autoscaling, la détection de fichiers via PATH_INFO, et deux bugs dans l’API expérimentale des extension workers (toutes les advisories).

Quelle gravité ?

Aucune de ces vulnérabilités n’est classée critique, et la plupart ne comptent qu’avec une configuration particulière. La faille de gravité haute suppose un attaquant qui peut déjà déposer des fichiers sous votre document root, puis les appeler avec un chemin forgé. La plupart des failles moyennes demandent quelque chose que votre installation n’a peut-être pas : des règles de chemin utilisées pour le contrôle d’accès, des variables env qui portent une identité, {host} dans un chemin de fichier, des named routes, des règles match de worker, des applications embarquées, un serveur Windows ou une variable HOME absente.

Les failles qui touchent la configuration par défaut mènent à une redirection vers un autre site, à un HTTP_HOST usurpé ou à un déni de service. Aucune ne permet d’exécuter du code sur une installation par défaut. En pratique, réunir les bonnes conditions pour les exploiter est difficile.

Ce n’est pas une raison pour attendre. Certains de ces bugs se combinent avec des pratiques courantes, comme faire confiance à HTTP_HOST pour construire des liens ou à REMOTE_USER pour identifier les utilisateurs, et une fois une advisory publiée, tout le monde peut la lire. Mettre à jour reste le correctif le moins cher que vous aurez.

Plus rapide, et moins de surprises

La sécurité n’était pas le seul chantier. Sur le chemin critique, FrankenPHP ne copie plus les chemins déjà enracinés pour calculer SCRIPT_FILENAME (#2794), convertit les noms d’en-têtes peu courants en une passe et une allocation (#2795), et découpe les en-têtes de réponse avec une recherche d’octets (#2796). Caddy pré-dimensionne ses buffers pour le nettoyage des chemins, la réécriture des en-têtes et weighted_round_robin (#7842), et la 2.11.8 accélère l’accès aux en-têtes dans le proxy (#8025).

Quelques correctifs de FrankenPHP à connaître :

  • les ressources persistantes, comme les connexions PDO::ATTR_PERSISTENT, sont détruites quand un thread PHP s’arrête. Avec PHP 8.4 et plus, chaque redémarrage de worker les laissait fuir, jusqu’à ce que la base de données refuse les nouvelles connexions (#2812) ;
  • un corps de requête qui dépasse le max_size de request_body reçoit maintenant une 413, au lieu d’arriver tronqué à PHP (#2785) ;
  • un script impossible à ouvrir renvoie une 404, comme php-fpm, au lieu d’une 200 avec une erreur fatale (#2791) ;
  • $_SERVER['SERVER_ADDR'] est maintenant renseigné, comme avec nginx et Apache (#2809).

PHP rend le web plus sûr

On voit souvent PHP comme le langage qu’il faut protéger. Cette fois, le travail sur PHP a protégé tous les autres. Le découpage des chemins, les variables CGI et la gestion du Host sont de vieux pièges de PHP. En les creusant pour FrankenPHP, on a corrigé Caddy, et tous ses utilisateurs en bénéficient, quel que soit le langage qu’ils servent. Chez Les-Tilleuls.coop et Tilia, nous maintenons FrankenPHP, contribuons à PHP et faisons partie de l’équipe sécurité de la PHP Foundation : nous corrigeons ces pièges là où ils naissent, dans le serveur web.

La prochaine étape se joue dans PHP lui-même. Plusieurs bugs du mode worker corrigés cette année, comme les destructeurs qui écrivent dans la réponse du client suivant, viennent du fait que FrankenPHP réinitialise l’état des requêtes en allant chercher directement dans le moteur. Le RFC request lifecycle hooks, en brouillon pour PHP 8.7, donne à tous les SAPI qui ont un mode worker une façon propre de le faire, et permet aux extensions comme Xdebug ou aux APM de savoir quand une requête commence et se termine.

Mettez à jour vos versions !

La plupart des changements sont invisibles, mais quelques comportements non sécurisés sont désormais refusés : les requêtes avec un hôte invalide ou un chemin de script ambigu reçoivent une 400, les valeurs env FastCGI l’emportent sur les en-têtes de la requête, les réglages client_auth contradictoires bloquent le démarrage, et un corps qui dépasse max_size reçoit une 413 dès que son Content-Length est connu. En mode worker, FrankenPHP ne réinitialise plus, entre deux requêtes, que l’état construit à partir de la requête HTTP. Appelez error_clear_last() ou libxml_clear_errors() si vous dépendiez de l’ancien comportement. Lisez les release notes avant de mettre à jour.

Besoin d’aide ? Ensemble avec Les-Tilleuls.coop, Tilia aide les équipes à sécuriser PHP en production :

  • audit et migration de votre infrastructure PHP vers FrankenPHP ;
  • support en production et maintenance de sécurité pour FrankenPHP, Caddy et Mercure, par les personnes qui écrivent les correctifs ;
  • formations sur Caddy, FrankenPHP, Symfony et API Platform.

Ce travail existe parce que des entreprises financent l’open source. Si FrankenPHP ou Caddy font tourner votre activité, sponsorisez leurs mainteneurs.

Kevin Dunglas
Kevin Dunglas CEO & technical director

Mots-clés Anthropic, Caddy, FrankenPHP, PHP, Sécurité