Les scanners IA réveillent notre dette technique
Les scanners de sécurité IA transforment des décennies de bugs dormants en failles critiques. Comment trier le backlog sans se noyer.

Un rapport de bug est arrivé dans le gestionnaire de tickets du projet PHP en 2024. Il décrivait une condition de dépassement de mémoire (out-of-memory) — le genre de chose qui fait planter un processus, agaçant mais, dans la hiérarchie informelle des bugs, un simple haussement d'épaules. Il est resté là. Puis, lors d'un audit indépendant de PHP sponsorisé par OpenAI, un chercheur de la société de sécurité Calif, s'appuyant sur des scanners de sécurité IA et sur sa solide expertise, a passé environ deux semaines à transformer ce plantage gênant en exécution de code à distance. N'importe quel serveur faisant tourner une version vulnérable de PHP pouvait être contraint d'exécuter le code d'un attaquant ou de révéler des données sensibles. Les sites dans le rayon d'impact comprenaient la NASA, Wikipédia et la Maison-Blanche, car WordPress et Drupal tournent sous PHP, et PHP fait tourner une part étonnante du web.
Le correctif a été publié avant que quiconque de malveillant n'arrive, donc l'histoire s'est bien terminée. Mais les chercheurs impliqués répètent la même chose : ce n'est que le début, pas la fin. Quarante ans de code écrit par des humains contiennent un océan de bugs que nous avons collectivement classés dans la catégorie « crashs, on ne corrige pas », et les outils d'IA ont fait s'effondrer le coût pour découvrir lesquels de ces crashs sont en réalité des armes chargées. J'ai passé ces dernières semaines à discuter avec des personnes qui gèrent la sécurité de systèmes réels pour comprendre ce que cela change concrètement. Ce qui suit n'est pas une étude, mais un recueil d'erreurs que j'ai déjà vu des équipes commettre, et de ce que ceux qui s'en sont bien sortis ont fait à la place.
Erreur n°1 : classer les bugs de crash comme « désagréables mais inoffensifs »
Le bug PHP au cœur de cette histoire n'était pas caché. Il a été signalé, reconnu, puis déprioritisé, parce qu'il ne provoquait « que » une erreur de dépassement de mémoire. C'est l'échec de triage le plus courant dans les organisations logicielles matures, et le développement d'exploits assisté par IA vient de le rendre coûteux. Le professeur Suman Jana, de Columbia, qui a travaillé précisément sur cette classe de recherche, a résumé la dynamique sans détour dans des interviews autour de la divulgation : à tout moment, chaque grand projet a un tas de rapports de crash que personne ne prend au sérieux, parce que transformer un crash en exploit demande de longues chaînes de raisonnement fastidieuses et hyper spécifiques. Les humains évitent ce travail. Les modèles, eux, ne s'ennuient pas.
Voici la version mécanique de la raison pour laquelle la distinction compte. Un bug mémoire qui corrompt le tas avec des données non contrôlées se contente généralement de tuer le processus : l'allocateur remarque une incohérence et interrompt l'exécution, ou le processus déréférence une valeur aberrante et plante en segmentation. Mais le même bug, alimenté avec une entrée soigneusement dimensionnée et façonnée, devient une primitive d'écriture :
void handle_request(const char *input, size_t len) {
char *buf = malloc(64);
memcpy(buf, input, len); /* len never validated */
/* len = 65: heap metadata trashed -> likely abort, filed as 'crash' */
/* len = attacker-tuned: adjacent object's function pointer overwritten */
process(buf); /* calls through the corrupted pointer -> their code runs */
free(buf);
}
La frontière entre ces deux issues ne tient pas au bug, elle tient à l'entrée. Déterminer quels bits écraser, dans quel ordre, pour faire atterrir l'exécution à une adresse choisie est fastidieux mais mécanique, et « fastidieux mais mécanique » correspond exactement au cœur de ce en quoi les modèles de langage excellent. La correction de cette erreur est d'abord conceptuelle avant d'être technique : arrêtez d'utiliser « ça plante seulement » comme signal de gravité pour du code à mémoire non sûre. Un crash en C ou C++ (ou dans le cœur C d'un interpréteur comme PHP) est un exploit qui n'a pas encore été développé. La nouvelle question de triage n'est pas « est-ce que ça plante ? » mais « la corruption est-elle influencée par l'attaquant ? » Si une entrée non fiable contrôle la taille, le décalage ou le contenu d'une mauvaise écriture, supposez l'exploitabilité et priorisez en conséquence, même si personne n'a démontré d'exploit fonctionnel. Surtout dans ce cas, car les exploits démontrés prennent désormais des semaines, et non des années.
Erreur n°2 : traiter la sortie des scanners comme un backlog
Le second mode d'échec apparaît dès qu'une équipe fait tourner des scanners modernes assistés par IA sur un parc qui comprend du code vieux de vingt ans. L'outil renvoie des centaines de résultats, le tableau de bord vire au rouge, et l'équipe fait ce que les backlogs l'ont habituée à faire : trier par score de gravité et commencer par le haut. Six mois plus tard, quarante problèmes ont été corrigés, le compteur a augmenté, et le moral est en miettes. Les ingénieurs que j'ai interrogés et qui gèrent bien la situation ont tous dit en substance la même chose : la sortie d'un scanner IA n'est pas un backlog, c'est du renseignement brut, et elle a besoin d'une passe de tri par un analyste avant qu'un seul ticket ne soit créé.
Un triage efficace dans ce régime pose des questions sur les chemins d'exploitation, pas sur les scores de gravité :
- Le code vulnérable est-il accessible depuis une entrée non fiable ? Un débordement de tas dans une fonction qui ne traite qu'un fichier de configuration local n'a rien à voir avec le même débordement dans la gestion des requêtes.
- Est-il exposé sur Internet aujourd'hui ? Pas « devrait-il l'être » : l'est-il. Vérifiez les configurations des répartiteurs de charge et le DNS, pas le schéma d'architecture.
- L'entrée contrôle-t-elle la corruption ? Taille, décalage ou contenu de l'accès incorrect. C'est la frontière crash-versus-exploit de la section précédente.
- Que rapporte un exploit ? Un crash dans un worker isolé qui redémarre automatiquement n'a pas la même portée qu'un crash dans le processus qui détient les identifiants de la base de données.
- Existe-t-il un exploit dans la nature ou un article technique public ? Cela fait passer n'importe quel élément en tête de file, quel que soit son score.
Ce dernier point mérite d'être souligné. Des scores comme le CVSS ont été conçus pour un monde où les humains trouvaient des bugs à un rythme humain, et ils surpondèrent systématiquement la gravité théorique par rapport à l'accessibilité. Quand un scanner peut générer une gravité théorique à l'échelle industrielle, on se noie. Les équipes qui s'en sortent le mieux ont inversé le pipeline : accessibilité et exposition d'abord, gravité ensuite, et tout ce qui est inaccessible par construction est documenté et clos plutôt que corrigé, car un correctif livré porte son propre risque de régression, et vous aurez besoin de ce budget de risque pour les problèmes qui comptent. C'est aussi là que les vérifications de compréhension du code par IA prouvent leur utilité côté défensif : le même outillage qui trouve le bug peut souvent répondre à « montre-moi tous les chemins d'appel depuis la frontière réseau jusqu'à cette fonction » en quelques minutes, une question qui prenait autrefois une journée entière à un ingénieur senior par problème.

Erreur n°3 : supposer que les correctifs peuvent suivre le rythme de la découverte
Voici une asymétrie qui devrait être affichée en poster dans chaque salle d'équipe sécurité. Trouver le chemin d'exploitation du bug PHP a pris à un chercheur compétent, assisté par IA, environ deux semaines. Développer le correctif a demandé quatre ingénieurs pendant deux mois. Trouver est un problème de recherche : il suffit d'un chemin. Corriger est un problème de type preuve : il faut fermer tous les chemins, et faire en sorte que les milliards d'installations non attaquées continuent de fonctionner exactement comme avant. Le travail du défenseur est strictement plus difficile que celui de l'attaquant, et l'IA réduit bien plus vite l'écart côté attaquant que côté défenseur.
Les équipes qui ignorent cette asymétrie font l'un de deux mauvais paris. Soit elles précipitent les correctifs pour suivre la pression de la divulgation et livrent des régressions (le correctif PHP touchait aux éléments internes de l'interpréteur utilisés par environ les trois quarts des sites web qui publient leur stack, donc le rayon d'impact d'un mauvais correctif est d'ampleur civilisationnelle), soit elles désespèrent et ne font rien en attendant un correctif parfait. Les praticiens qui ont bien géré cette fenêtre ont fait quelque chose de moins héroïque et de plus efficace : ils ont séparé « atténuer maintenant » de « corriger correctement ». Les mesures d'atténuation sont peu coûteuses, rapides et réversibles : restreindre l'endpoint concerné au niveau du reverse proxy, déployer une règle WAF qui bloque la forme de l'entrée de l'exploit, désactiver le chemin de code vulnérable derrière un feature flag, réduire le nombre de personnes pouvant atteindre le service pendant que le vrai correctif mûrit. J'ai déjà écrit sur la façon dont la décision entre correctif et atténuation est un véritable arbitrage plutôt qu'un échec moral, et cet incident est cette leçon à l'échelle planétaire.
Le corollaire que personne ne veut entendre : votre délai moyen de mitigation est désormais la métrique qui compte, parce que la fenêtre entre « bug trouvé par des chapeaux blancs » et « bug trouvé par quelqu'un de pire » se réduit. Dans le cas PHP, la fenêtre n'est restée ouverte que parce que les chercheurs ont été responsables et que la fondation a agi vite. La bibliothèque de compression Brotli a raconté une histoire moins flatteuse à la même période : un bug comparable est resté dans le code pendant huit ans, a été corrigé dans git mais non publié pendant huit mois, et a été redécouvert indépendamment par au moins quatre chercheurs. Quand la découverte est bon marché et parallèle, « quelqu'un finira bien par le trouver » cesse d'être une pensée rassurante et devient une garantie de planification.
Erreur n°4 : faire confiance à un correctif parce qu'un LLM l'a écrit
Le contre-argument optimiste à tout cela va ainsi : d'accord, l'IA trouve les bugs plus vite que nous ne pouvons les corriger aujourd'hui, mais elle les corrigera aussi. Anthropic a publié en octobre un outillage allant précisément dans ce sens : un LLM génère un correctif, puis un second modèle génère une preuve vérifiable par machine que le correctif préserve le comportement voulu, et un petit vérificateur de preuves, simple et digne de confiance, valide la preuve. Le vérificateur est le seul composant qui doit être correct, et il peut l'être, car il fait presque rien. Jana est sincèrement enthousiaste à propos de cette direction, et je comprends pourquoi : c'est le premier chemin crédible vers une correction presque aussi automatisable que la découverte.
Mais j'ai vu deux équipes adopter des workflows de « correctif de sécurité généré par IA », et les deux ont commis la même erreur : elles ont traité le correctif du modèle comme une solution plutôt que comme un candidat. Dans un cas, le correctif a fermé le débordement de tas signalé en ajoutant une vérification de longueur, et a déplacé le débordement vers un second chemin d'allocation, à six lignes de là, que le modèle n'avait pas modélisé. Le correctif a passé la revue parce qu'il paraissait évidemment correct, et c'est exactement le problème : les correctifs des LLM sont fluides d'une façon que les correctifs humains ne sont pas, et cette fluidité réduit l'esprit critique. Les équipes qui ont tiré profit des correctifs générés ont ajouté trois verrous, dans cet ordre :
- Une suite de non-régression qui inclut l'entrée de l'exploit. La preuve de concept qui a déclenché le problème devient un test permanent. Si le scanner vous a fourni une entrée qui fait planter le programme, c'est votre premier nouveau cas de test, gratuitement.
- Une chasse aux variantes. Avant la fusion, quelqu'un (ou quelque chose) répond : où ce même motif apparaît-il ailleurs ? Corriger une seule instance d'un motif systémique, c'est ce qui donne naissance aux suites de CVE.
- Une vérification indépendante de l'invariant, et non du diff. Énoncez ce qui doit désormais être vrai (« aucun chemin depuis la gestion des requêtes n'atteint memcpy avec une longueur non validée ») et vérifiez cela, avec des tests, de l'analyse statique ou un outillage formel. Relire uniquement le diff revient à relire les devoirs du modèle.
La vérification formelle aidera ici à mesure qu'elle mûrira, mais notez la répartition des rôles qui la rend fiable : les parties créatives ont le droit d'halluciner, parce que l'arbitre final est mécanique et petit. C'est le principe de conception à retenir, quel que soit votre outillage. Tout ce que produit un LLM (découverte, correctif ou preuve) doit aboutir à quelque chose qui ne partage pas ses modes de défaillance. Si votre étape de vérification est un autre prompt, vous n'avez rien vérifié.

Erreur n°5 : supposer que quelqu'un d'autre a audité le code
Sous chaque leçon technique se cache une leçon économique. PHP est une infrastructure à l'échelle d'un service public : il alimente WordPress, Drupal et une longue traîne du web qui comprend des agences spatiales et des sites gouvernementaux, et sa sécurité repose sur la PHP Foundation, un petit organisme financé, ainsi que sur des bénévoles. Le mythe confortable de l'open source a toujours été « avec assez de regards, tous les bugs deviennent superficiels ». Ce qui s'est réellement passé pendant trente ans, c'est que chacun supposait que ces regards appartenaient à quelqu'un d'autre. L'IA fournit désormais ces regards, sans discernement, aux attaquants comme aux défenseurs, ce qui signifie que le mythe est enfin rendu vrai, mais de façon accusatoire et sous contrainte de délai.
La divulgation de PHP2Shell a fonctionné grâce à un arrangement inhabituel : OpenAI a sponsorisé un audit externe, une société bien dotée a fait le travail, et Wiz a aidé à confirmer et à étendre les découvertes. C'est de la charité, pas un système, et cela ne passe pas à l'échelle des milliers de projets critiques mais sous-financés de votre arbre de dépendances. Si la production de votre organisation dépend d'un interpréteur, d'un framework ou d'une bibliothèque open source (et c'est le cas), les correctifs qui comptent sont des tâches ingrates : financer les fondations, sponsoriser des audits, contribuer en heures d'ingénieur, et quand vous faites tourner vos propres scanners et trouvez quelque chose dans une dépendance, le signaler en amont plutôt que de patcher votre fork et passer à autre chose. L'alternative est le monde que prédit Matthew Green, de Johns Hopkins : rien de structurel ne changera tant qu'un incident assez grave n'aura pas bouleversé les gens ordinaires et forcé la main. Son conseil en attendant est peu flamboyant et juste : quand la fenêtre de mise à jour apparaît, installez-la maintenant, pas la semaine prochaine. Nous avons appris la même leçon à la dure lors du dernier grand cycle de divulgation PHP, quand couper la prise a battu les correctifs astucieux pour les équipes incapables de patcher assez vite.
Les regards sont arrivés. Ils ne sont simplement pas regardants sur le camp dans lequel ils se trouvent.
Le guide du lundi matin pour les vulnérabilités révélées par l'IA
Tout ce qui précède se résume en une courte liste de choses que je ferais réellement ce trimestre si je dirigeais une organisation d'ingénierie avec du code legacy en production, et si vous lisez ceci, vous avez du code legacy en production :
- Patchez agressivement et mesurez le délai. Votre latence de mise à jour est désormais un contrôle de sécurité avec un seuil de sécurité qui se réduit. Connaissez votre chiffre avant d'en avoir besoin.
- Retriez votre backlog existant de bugs de crash avec la question de l'exploitabilité, et non le score de gravité : l'entrée non fiable contrôle-t-elle la corruption ? Tout élément à mémoire non sûre et accessible est promu.
- Lancez vous-mêmes un scan assisté par IA, sur votre code le plus ancien d'abord. Le coût marginal de la redécouverte est désormais proche de zéro, ce qui signifie qu'il faut supposer que tout ce qui est trouvable dans votre surface d'attaque le sera. Trouvez-le en premier.
- Séparez l'atténuation de la correction dans votre processus d'incident. Préparez le playbook à l'avance : règles de proxy, feature flags, restriction d'accès. Gagner du temps est une stratégie légitime ; prétendre que vous n'en avez pas besoin ne l'est pas.
- Conditionnez les correctifs générés à des tests d'entrée d'exploit et à des chasses aux variantes, jamais à la propreté apparente du diff.
- Investissez de l'argent ou du temps dans les projets amont dont vous dépendez. Des audits sponsorisés ont trouvé celui-ci. Ce n'est pas une coïncidence : c'est le modèle qui fonctionne, et il doit cesser d'être exceptionnel.
On peut débattre sérieusement de la question de savoir si cette ère consiste à « rembourser la dette technique » ou à nous embarquer pour un tapis roulant sans fin de correctifs sans valeur fonctionnelle. Probablement les deux : chaque correctif non trivial porte le risque du prochain bug subtil, et aucun système n'est jamais terminé. Mais je préfère vivre dans un monde où la dette est réclamée par les auditeurs et les chapeaux blancs, plutôt que dans celui où l'on fait semblant que les sables mouvants sont du roc parce que rien ne nous a encore engloutis. PHP2Shell s'est terminé par un correctif et un soupir de soulagement. Le prochain est déjà dans la file de crashs de quelqu'un, classé comme inoffensif. Allez regarder la vôtre.


