Rien ne tourne quand vous demandez une page

Cette semaine, le projet PHP a publié les correctifs de PHP2Shell, et son avis disait que l’attaque peut se déclencher « before any application code runs »1. J’ai écrit ce que cela signifie pour PHP dans PHP : vingt-neuf ans à laisser la porte ouverte. Cet article parle de l’autre moitié de la réponse. Et si rien ne tournait du tout ?

C’est ainsi que fonctionne ce blog. Quand vous avez demandé cette page, aucun programme à moi ne s’est lancé, n’a lu de base de données, n’a vérifié de connexion ni construit la moindre page HTML. Un fichier qui existait déjà a été renvoyé. C’est tout.

Voici à quoi ressemble cette différence, mesurée le même jour, sur un WordPress d’origine installé à neuf dans un labo et sur ce site tel qu’il est en ligne aujourd’hui :

WordPress 7.1.3 d’origineCe site
Routes de l’API REST1330
Routes REST qui acceptent des écritures620
Méthodes XML-RPC800
Fichiers exécutés sur le serveur1 514 PHP0
Fichiers modifiables par l’utilisateur qui sert le site3 785 sur 3 785aucun : rien ne tourne pour écrire
Base de données12 tables, compte applicatif avec tous les privilègesaucune
Page de connexionwp-login.php, en ligneaucune ; le chemin renvoie 404
Un POST, PUT ou DELETE sur la page d’accueilPHP s’exécute et affiche la page quand mêmerefusé en bordure avec 405 ; rien ne s’exécute

Aucun de ces zéros n’est un réglage que j’ai désactivé. Ce sont des zéros parce que ce qu’ils comptent n’existe pas ici. On ne peut pas attaquer une page de connexion qui n’a jamais été construite.

Ce qu’un WordPress d’origine met sur Internet

Je ne voulais pas argumenter de mémoire, alors j’ai mesuré. Les images de conteneur officielles wordpress et mariadb, lancées dans podman sur ma propre machine et liées à localhost, installées sans rien ajouter : aucune extension au-delà de ce qui est livré, le thème par défaut, un administrateur. WordPress 7.1.3 sur PHP 8.3.36. Ensuite, j’ai compté ce qui répond.

Ce qui répond à une requête : WordPress face à un site statiqueUne requête WordPressUne requête vers ce siteInternetserveur webPHP exécute du code, à chaque requêtecœur de WordPress : 1 514 fichiers PHPplus chaque extension et thème133 routes REST, 80 méthodes XML-RPCtout modifiable par le même utilisateurbase de données, ALL PRIVILEGESconnexion, admin, cron, téléversementsInternetpériphérie Cloudflareun fichier est renvoyé6 343 fichiers, construits d'avanceaucun code à nous ne tournePOST, PUT, DELETE : 405rien à écrire sur le serveuraucune base de donnéesni connexion, ni admin, ni cron
Ce qui répond à une requête. Sur WordPress, chaque page vue exécute PHP à travers le cœur, les extensions et une base de données, sur des fichiers que le même utilisateur peut réécrire. Ici, un fichier fini est renvoyé et rien ne tourne.

L’API REST est la plus grande surface à elle seule. Cinq espaces de noms, 133 routes, 257 paires route et méthode, et 62 routes qui acceptent une écriture d’une sorte ou d’une autre. Elle est arrivée dans le cœur avec WordPress 4.7, « with REST API endpoints for posts, comments, terms, users, meta, and settings »2. Et on vous dit de ne pas la couper : « You should not disable the REST API; doing so will break WordPress Admin functionality that depends on the API being active. »3 Donc elle reste active.

Ce qui est exposé par défautCe qu’en dit la documentation de WordPress elle-même
XML-RPC, 80 méthodesactif par défaut depuis 3.5 : « Enable XML-RPC by default, remove Writing Settings option »4
l’API REST, 133 routes« content that is public on your site is generally publicly accessible via the REST API »5
la liste des utilisateursGET /wp/v2/users renvoie le nom affiché, le slug, le lien et l’avatar de chaque auteur dans sa vue par défaut6
wp-login.php et xmlrpc.php« xmlrpc.php is a frequent brute‑force target » ; « Apply rate limits to /wp-login.php and /xmlrpc.php globally. »7
wp-cron.php« WP-Cron works by checking, on every page load, a list of scheduled tasks to see what needs to be run. »8
l’éditeur de thèmes et d’extensions« This is often the first tool an attacker will use if able to login, since it allows code execution. »9

La liste des utilisateurs mérite qu’on s’y arrête. Dans mon labo, une requête anonyme vers ce point d’accès a renvoyé le seul compte administrateur, avec son nom affiché et son slug. Quelqu’un qui ne s’est jamais connecté apprend quels sont les comptes du site avant d’avoir tenté quoi que ce soit, sur une installation neuve, par conception. Personne n’a eu besoin de mal le configurer.

Ensuite, les fichiers. L’image officielle fait tourner Apache sous www-data, et www-data possède toute la racine web. Les 3 785 fichiers, dont les 1 514 fichiers PHP, sont modifiables par le même utilisateur qui exécute le code. Tous les fichiers. Le code compris. wp-config.php, avec le mot de passe de la base dedans, se trouve dans la racine web en mode 644. Le compte de base de données qu’utilise WordPress détient ALL PRIVILEGES sur sa base.

C’est le modèle à processus unique de l’article sur PHP, en ligne et sans modification, tiré de l’image officielle. Ce n’est pas un site mal tenu. C’est le point de départ de tout site WordPress, avant même que la première extension soit installée.

Et c’est des extensions que vient l’essentiel. Patchstack a compté 11 334 nouvelles vulnérabilités dans l’écosystème WordPress en 2025. « 91% of new vulnerabilities were found in plugins, and 9% were found in themes. There were only 6 vulnerabilities reported in the WordPress core. »10 Près de la moitié n’avaient aucun correctif au moment de leur publication, et le délai médian avant l’exploitation de masse, pour celles qui intéressaient les attaquants, était de cinq heures10.

Ce que ce site met sur Internet

Ce blog, c’est 6 343 fichiers. Les voici tous, par type :

Type de fichierNombreCe que c’est
.html3 486les pages, en cinq langues
.xml1 576flux RSS et plans de site
.svg1 181diagrammes, couvertures, icônes
.png46captures d’écran
.json10l’index de recherche et les tampons de build
.js4de petits scripts qui tournent dans votre navigateur, pas sur le serveur
.css, polices et le reste40la mise en forme, et les fichiers joints aux articles
Exécutés sur le serveur0

Il y a quelques fichiers .py et .sh là-dedans. Ce sont des scripts joints aux articles, que les lecteurs peuvent télécharger, et le serveur les envoie comme du texte. Rien sur le serveur ne les exécute. Il n’y a rien sur le serveur pour les exécuter.

Il est déployé sur Cloudflare en ressources statiques, sans aucun script Worker. La configuration de déploiement n’a pas de point d’entrée, et la documentation de Cloudflare dit ce que cela signifie : « The main key is optional for assets-only Workers. »11 Quand un chemin demandé correspond à un fichier, « that file will be served » sans invoquer de code Worker, et « If no Worker script is present, a 404 Not Found response is returned. »12 Cela ne coûte rien non plus : « Requests to static assets are free and unlimited. »13

Cloudflare est un choix, pas une obligation. La sortie est un dossier de fichiers, et n’importe quel serveur HTTPS capable d’envoyer un fichier peut la servir : nginx, Caddy, un Apache limité aux fichiers statiques, ou un bucket de stockage objet derrière un CDN. Les liens sont relatifs, donc le même build se sert tel quel depuis la racine d’un domaine ou depuis un sous-répertoire ; vérifié en servant le site construit sous /preview/ et en contrôlant que les 592 références locales de six pages se résolvaient toutes. Changer d’hébergeur, c’est copier un dossier. La seule pièce propre à Cloudflare est le fichier _headers ; ailleurs, les mêmes en-têtes vont dans la configuration du serveur. Ce qui suit, c’est la propriété qui compte. Ce qui sert le site n’a qu’à envoyer des fichiers, donc n’a jamais à exécuter votre code.

Le site en ligne répond donc ainsi :

$ for m in POST PUT DELETE; do curl -s -o /dev/null -w "$m %{http_code}\n" -X $m https://blogs.damiendye.uk/en/; done
POST 405
PUT 405
DELETE 405

$ for p in wp-login.php xmlrpc.php wp-admin/ wp-json/; do curl -s -o /dev/null -w "%{http_code} $p\n" https://blogs.damiendye.uk/$p; done
404 wp-login.php
404 xmlrpc.php
404 wp-admin/
404 wp-json/

Rien ne répond. C’est voulu.

Les en-têtes de sécurité partent avec chaque fichier, depuis un simple fichier texte _headers dans le dépôt, que Cloudflare lit au déploiement14. La Content Security Policy verrouille chaque script, style, image, police et connexion sur cette seule origine. Je vais être franc sur le trou qu’elle a : script-src et style-src portent encore 'unsafe-inline', parce que le thème écrit quelques petits scripts dans la page. C’est le travail en attente sur ce fichier, et il est noté dans le dépôt exactement comme tel.

Et publier n’exige aucune porte. Sur WordPress, publier est une écriture par HTTP : l’éditeur, l’API REST et XML-RPC existent parce que c’est ainsi que les articles arrivent sur le serveur, et chacun d’eux est un chemin d’entrée. Ici, j’écris un article comme un fichier texte et je le pousse dans un dépôt Git, et le build téléverse des fichiers finis par l’API de déploiement de Cloudflare. Rien de ce qui sert le site n’accepte d’écriture depuis le web, parce que rien n’en a besoin. Le dépôt est privé, en plus. Demandez-le à GitHub sans mes identifiants et la réponse est une 404, donc les sources, les brouillons et l’historique ne sont pas non plus sur Internet.

La surface d’attaque est un compte, pas une impression

L’OWASP définit la surface d’attaque comme « the sum of all paths for data/commands into and out of the application, and the code that protects these paths »15, et dit que la manière de la réduire est « turning off features and interfaces that aren’t being used »15. Le National Cyber Security Centre britannique le dit plus crûment : « If would-be attackers can’t reach an interface, they can’t attack it. »16 Son programme Cyber Essentials en fait une exigence : « remove or disable unnecessary software (including applications, system utilities and network services) »17.

Alors comptez les chemins, par ce qu’ils sont plutôt que par leur nombre :

Chemin d’entrée ou de sortieWordPressSite statique
connexion et devinette de mots de passewp-login.php, XML-RPCaucun
interface d’administration/wp-admin/, avec un éditeur de codeaucune
API programmableREST et XML-RPC, avec écrituresaucune
code côté serveur à chaque requêtele cœur, le thème, chaque extensionaucun
base de donnéesoui, joignable depuis tout ce qui précèdeaucune
stockage modifiable sur le serveurles téléversements, et dans l’image d’origine, toutaucun
tâches planifiées déclenchées par les visiteurswp-cron.php au chargement de la pageaucune
code tiers à maintenir à jourchaque extension et chaque thème, sur le serveurles outils de build, hors du serveur
ce qu’une session volée rapporte à un attaquantle siterien où se connecter

Neuf lignes. Aucune n’exécute de code sur le serveur. Chaque fois pour la même raison. Le travail a été fait avant que quiconque demande une page.

L’avant-dernière ligne, c’est le vrai bémol, et le reste de cet article en parle. Un site statique exécute quand même du code. Simplement, il l’exécute ailleurs, une fois, avant la publication.

Comment Hugo garde le build honnête

Hugo est « a static site generator written in Go »18, donc le programme qui construit est lui-même compilé et sûr en mémoire, ce qui compte après l’article sur PHP. Sa page de sécurité trace la ligne exactement là où cet article la trace : le résultat « runs directly in the browser », mais « during development and site building, the hugo executable itself is the runtime environment. »19 Et sur la question du service, sans détour : « We have no online production server »19. Bien.

Dans le build, tout est verrouillé par défaut. « Hugo’s built-in security policy, which restricts access to os/exec, remote communication, and similar operations, is configured via allowlists. By default, access is restricted. »20

Le réglage par défaut de HugoCe qu’il empêche
« No arbitrary commands »19un template ne peut pas lancer de commande sur la machine
exec.allow limité à Sass, Go, Git, Node et PostCSS20seulement des outils nommés, avec des options fixes
funcs.getenv limité à ^HUGO_ et ^CI$20les templates ne peuvent pas lire vos secrets dans l’environnement
les requêtes distantes refusent les hôtes en adresse IP et localhost20un template ne peut pas atteindre votre propre réseau
modules vérifiés contre go.sum19une dépendance altérée fait échouer le build

Ce site en élargit un exprès. Il ajoute les variables de commit de la CI à funcs.getenv, pour que le tampon de build à /en/version.json puisse enregistrer quel commit a construit le site en ligne. C’est un choix fait et noté, et c’est tout l’intérêt d’une liste d’autorisation. Ce n’est donc pas une faille. C’est une décision.

Où le risque se déplace : le build

Retirez le serveur et le risque ne disparaît pas. Il se déplace vers ce qui construit les fichiers. Le cadre SLSA nomme exactement les deux menaces : une « build integrity threat », où un adversaire introduit « behavior to an artifact without changing its source code », et une « dependency threat », en « compromising some other artifact that the former depends on at build time »21.

Où le code tourne : WordPress, ce site et un build hors ligneOÙ LE CODE TOURNECE QUE LE PUBLIC ATTEINTWordPressle serveur public, à chaque requêtePHP, extensions, base de donnéesCe sitebuild Cloudflare, à chaque pushfichiers statiques seulementConstruit hors lignevotre propre machine, une foisfichiers statiques seulementle build fait toujours confiance à Hugo, au thème et à sa machine
Où le code tourne dans chaque modèle. WordPress l’exécute sur le serveur public pour chaque visiteur. Ce site l’exécute une fois par push dans le conteneur de build de Cloudflare. Construit hors ligne, il tourne une fois sur une machine qui vous appartient.

Ce site est dans la ligne du milieu, et je ne vais pas prétendre le contraire. Il se construit dans le conteneur de build de Cloudflare à chaque push sur le dépôt. Ce conteneur n’est pas le serveur qui vous répond, et il disparaît quand le build se termine. Mais pendant toute la durée du build, on lui confie tout ce qui finit sur le site.

Et le build avait deux failles, que j’ai trouvées toutes les deux en écrivant cet article. L’une est corrigée. L’autre est un choix.

FaillePourquoi c’est importantÉtat
le binaire Hugo était pris sur parole : le téléchargement du script lui-même n’avait aucune vérification de somme de contrôle, et en CI il sautait de toute façon le téléchargement et utilisait la copie installée dans l’image de build de Cloudflareun binaire substitué construirait ce qu’il voudraitcorrigé, 23 octobre 2026 : le build télécharge toujours Hugo lui-même, le vérifie contre le SHA-256 publié avec la version22, et refuse de construire en cas de différence
le thème suit la branche master de son projet amont à chaque buildun changement en amont peut modifier le site en ligne sans aucun commit iciouvert, par choix ; la correction est de l’épingler sur un commit, ou de le passer en module Hugo couvert par go.sum19

La première est le genre de chose qu’on ne trouve qu’en lisant son propre script de build ligne par ligne. Le script vérifiait bien la version. Mais une chaîne de version, c’est ce qu’un binaire dit de lui-même. Elle ne prouve rien sur les octets. Maintenant, ce sont les octets qui sont vérifiés :

Fetching Hugo Extended 0.167.0...
Hugo verified: sha256 0163f5c3deddac1f494a1629ddc40c65d18de9d5794facd98f7f96ac2c7d8957

Et une copie de la même archive avec un octet ajouté est refusée :

Refusing to build: hugo_extended_0.167.0_linux-amd64.tar.gz does not match the release checksum
  expected 0163f5c3deddac1f494a1629ddc40c65d18de9d5794facd98f7f96ac2c7d8957
  got      92682d765d9d34bc53d6f818d56e598d34b5b8267a1cc3b9971da0f53c792251

Le thème est un compromis délibéré, la fraîcheur plutôt que la reproductibilité, et il est noté comme tel. Mais c’est exactement la menace sur les dépendances que décrit SLSA, et l’honnêteté impose de le dire. Aucune des deux failles n’a jamais exposé le site en ligne à un visiteur. Les deux étaient de la confiance placée dans le build, et je préfère les lister plutôt que de laisser un lecteur les trouver.

Même la somme de contrôle a une limite. Elle prouve que le fichier téléchargé est celui de la page de la version. Elle ne prouve pas qui l’y a mis : il n’y a aucune signature parmi les fichiers de la version22. C’est de l’intégrité, pas de l’authenticité, et c’est le maximum que le projet propose aujourd’hui.

Construire hors ligne, une fois

La version la plus stricte sort le build de la machine de n’importe qui d’autre. Construisez sur une machine que vous possédez et contrôlez, vérifiez ce que vous avez construit, et n’envoyez que les fichiers finis. Rien sur Internet n’exécute jamais votre build, et l’hébergeur ne voit jamais rien d’autre que le résultat.

ÉtapePourquoi
installer la version exacte de Hugo et la vérifier contre le SHA-256 publiévous savez quel binaire a construit votre site
épingler le thème et les éventuels modules sur des commits, et committer go.sumle build ne change que quand vous le changez
construire dans un répertoire de sortie propre, sans --buildDraftsce que vous publiez est ce que vous vouliez publier
chercher tout ce qui est exécutable dans la sortie avant qu’elle parteun site statique ne devrait rien contenir que le serveur exécuterait
n’envoyer que le répertoire de sortie, avec un identifiant limité au déploiementl’hébergeur ne détient jamais vos sources, vos outils ni votre historique
servir depuis un hébergeur qui n’exécute aucun code pour les fichiers statiquesil ne reste rien à corriger au moment de servir

C’est quelques commandes, pas un projet. Et cela change l’endroit où un attaquant doit se trouver pour vous nuire. Pour modifier un site WordPress, il lui faut une requête au serveur. Pour modifier ce genre de site, il lui faut votre machine de build. C’est une cible bien plus petite, et c’est une cible que vous pouvez débrancher.

Dix mille pages, mesurées

La surface d’attaque est un coût du code exécuté à chaque requête. L’électricité en est un autre, et le disque aussi. J’ai donc mis les deux sous la même charge : 10 000 requêtes vers la page d’accueil, dix à la fois, depuis le même portable, en HTTP simple sur localhost pour qu’aucun des deux ne paie le TLS. D’un côté, le WordPress de base du labo. De l’autre, les vrais fichiers construits de ce site servis par nginx dans un conteneur, car je ne peux pas brancher un compteur sur la bordure de Cloudflare. J’ai lu le temps CPU de chaque conteneur dans son cgroup avant et après chaque passe, trois passes chacun.

10 000 requêtes de pagesWordPress de base 7.1.3Ce site sur nginx
Temps CPU, trois passes409 s, 530 s, 575 s0,15 s, 0,18 s, 0,19 s
Temps CPU par page41 à 57 ms15 à 19 µs
Pages par seconde168 à 23623 679 à 28 044
Mémoire sous charge302 à 310 Mio, plus 145 Mio pour la base15 Mio
Requêtes en échec00

C’est deux mille à trois mille cinq cents fois plus de temps processeur pour les mêmes dix mille pages. Chacune de ces secondes CPU a été tirée du secteur et transformée en chaleur. Le site statique a servi les 10 000 en à peu près le temps processeur que WordPress a passé sur trois ou quatre de ses pages.

Le disque raconte la même histoire :

Sur disque et en mémoireWordPress de baseCe site
Images de conteneur pour le faire tourner803 Mo WordPress, 334 Mo MariaDB166 Mo nginx, ou rien du tout sur Cloudflare
Le site lui-même115 Mo de racine web, pour un site vide114 Mo, cinq langues, chaque article et chaque diagramme
Base de données163 Mo de fichiers pour 0,72 Mo de donnéesaucune
Mémoire au repos170 Mio, plus 139 Mio pour la base13 Mio

Les chiffres WordPress sont ceux d’une installation neuve avec un article d’exemple. Les chiffres statiques sont ce blog entier.

Pour être juste envers WordPress, une extension de cache de pages complètes comblerait une grande partie de l’écart CPU. Mais regardez ce qu’est ce correctif. C’est une copie statique de chaque page, placée devant le PHP pour que le PHP ne tourne pas. Le remède, c’est exactement ce que défend cet article, boulonné sur le côté de ce qu’il combat, avec la page de connexion, l’API et la base de données toujours là derrière.

Ce que vous abandonnez

La liste honnête, parce qu’un site statique n’est pas le bon outil pour tout :

Vous abandonnezCe que fait ce site à la place
les commentairesaucun ; les lecteurs peuvent écrire par e-mail
la recherche côté serveurun index JSON construit avec le site, interrogé dans votre navigateur
les formulaires qui envoient au serveuraucun
les connexions et les espaces membresaucun ; tout ici est public
l’édition dans un navigateurles articles sont des fichiers texte dans un dépôt git
la publication instantanée sans buildun build prend une minute ou deux

S’il vous faut une boutique, un espace membres ou des comptes utilisateurs, il vous faut quelque chose qui tourne sur un serveur, et cet article ne vous dit pas le contraire. Ce qu’il vous dit, c’est qu’un blog, un site vitrine ou une documentation n’a besoin de rien de tout cela. La plupart des sites WordPress sont l’un des trois. Soyez honnête sur ce qu’est le vôtre.

Construisez-le une fois et servez-le inerte

Ce qui me sidère, c’est la quantité de risque qu’on porte sans aucune raison. Une page d’entreprise qui change deux fois par an, avec une base de données, une page de connexion, une API à 62 routes en écriture et un lanceur de tâches planifiées qui se déclenche à chaque visite. Tout cela pour produire le même HTML qu’hier. Tous les jours.

Ce n’est pas une décision technique. C’est une habitude, et c’est une habitude que quelqu’un facture. L’hébergement, les extensions, les modules de sécurité, le nettoyage quand ça tourne mal. Le prix affiché, c’est une offre WordPress infogérée. Ce que cela coûte vraiment, c’est chacun de ces chemins gardé ouvert, corrigé et surveillé, pour un contenu qui aurait pu être un dossier de fichiers.

La norme que j’impose à ce site est simple. Rien ne tourne qui n’en a pas besoin. Chaque faille est notée là où la prochaine personne la trouvera, y compris les deux du build, et l’une d’elles est déjà fermée. Il ne s’agit pas d’être malin. Une norme, c’est ce qu’on tient quand personne ne vérifie, et personne ne vérifie un blog jusqu’au jour où il se met à servir le code de quelqu’un d’autre.

Alors construisez-le une fois, vérifiez ce que vous avez construit, et servez-le inerte. Ce qui reste à attaquer est sur votre machine, là où il doit être.


  1. PHP — News archive 2026, 21st October — « We consider the risk highest for mod_php, with a demonstrated exploit where an unauthenticated request can trigger the attack before any application code runs ». ↩︎

  2. WordPress 4.7 release notes — « WordPress 4.7 comes with REST API endpoints for posts, comments, terms, users, meta, and settings. » ↩︎

  3. WordPress REST API Handbook — Frequently Asked Questions — « You should not disable the REST API; doing so will break WordPress Admin functionality that depends on the API being active. » ↩︎

  4. WordPress 3.5 release notes — « Enable XML-RPC by default, remove Writing Settings option ». ↩︎

  5. WordPress REST API Handbook — « content that is public on your site is generally publicly accessible via the REST API ». ↩︎

  6. WordPress REST API reference — Users — GET /wp/v2/users, contexte par défaut « view » ; name, slug, link et avatar_urls dans le contexte view. ↩︎

  7. WordPress — Brute force attacks — « xmlrpc.php is a frequent brute‑force target » ; « Apply rate limits to /wp-login.php and /xmlrpc.php globally. » ↩︎

  8. WordPress Plugin Handbook — Cron — « WP-Cron works by checking, on every page load, a list of scheduled tasks to see what needs to be run. » ↩︎

  9. WordPress — Hardening WordPress — l’éditeur du tableau de bord « is often the first tool an attacker will use if able to login, since it allows code execution. » ↩︎

  10. Patchstack — State of WordPress Security in 2026 — données 2025 : 11 334 nouvelles vulnérabilités ; 91 % dans les extensions, 9 % dans les thèmes, 6 dans le cœur ; 46 % non corrigées à temps pour la divulgation ; 5 heures en médiane avant l’exploitation de masse. ↩︎ ↩︎

  11. Cloudflare — Wrangler configuration — « The main key is optional for assets-only Workers. » ↩︎

  12. Cloudflare — Workers static assets — un fichier correspondant « will be served » sans invoquer de code Worker ; « If no Worker script is present, a 404 Not Found response is returned. » ↩︎

  13. Cloudflare — Static assets billing and limitations — « Requests to static assets are free and unlimited. » ↩︎

  14. Cloudflare — Static assets headers — en-têtes définis par « a plain text file called _headers ». ↩︎

  15. OWASP — Attack Surface Analysis Cheat Sheet — « the sum of all paths for data/commands into and out of the application, and the code that protects these paths » ; la réduire en « turning off features and interfaces that aren’t being used ». ↩︎ ↩︎

  16. NCSC — Making compromise difficult, 2.2 Reduce attack surface — « If would-be attackers can’t reach an interface, they can’t attack it. » ↩︎

  17. NCSC — Cyber Essentials : Requirements for IT infrastructure v3.2 — « remove or disable unnecessary software (including applications, system utilities and network services) ». ↩︎

  18. Hugo — Introduction — « Hugo is a static site generator written in Go, optimized for speed and designed for flexibility. » ↩︎

  19. Hugo — Security model — « during development and site building, the hugo executable itself is the runtime environment » ; « We have no online production server » ; « No arbitrary commands » ; une somme de contrôle go.sum qui ne correspond pas fait échouer le build. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  20. Hugo — Configure security — « By default, access is restricted » ; listes d’autorisation par défaut pour exec, getenv et http. ↩︎ ↩︎ ↩︎ ↩︎

  21. SLSA v1.2 — Threats — menace sur l’intégrité du build : « behavior to an artifact without changing its source code » ; menace sur les dépendances : « compromising some other artifact that the former depends on at build time ». ↩︎

  22. Hugo v0.167.0 release — 36 fichiers, dont hugo_0.167.0_checksums.txt (SHA-256) ; aucun fichier de signature, de SBOM ni d’attestation. ↩︎ ↩︎