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’origine | Ce site | |
|---|---|---|
| Routes de l’API REST | 133 | 0 |
| Routes REST qui acceptent des écritures | 62 | 0 |
| Méthodes XML-RPC | 80 | 0 |
| Fichiers exécutés sur le serveur | 1 514 PHP | 0 |
| Fichiers modifiables par l’utilisateur qui sert le site | 3 785 sur 3 785 | aucun : rien ne tourne pour écrire |
| Base de données | 12 tables, compte applicatif avec tous les privilèges | aucune |
| Page de connexion | wp-login.php, en ligne | aucune ; le chemin renvoie 404 |
| Un POST, PUT ou DELETE sur la page d’accueil | PHP s’exécute et affiche la page quand même | refusé 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.
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éfaut | Ce qu’en dit la documentation de WordPress elle-même |
|---|---|
| XML-RPC, 80 méthodes | actif 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 utilisateurs | GET /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 fichier | Nombre | Ce que c’est |
|---|---|---|
.html | 3 486 | les pages, en cinq langues |
.xml | 1 576 | flux RSS et plans de site |
.svg | 1 181 | diagrammes, couvertures, icônes |
.png | 46 | captures d’écran |
.json | 10 | l’index de recherche et les tampons de build |
.js | 4 | de petits scripts qui tournent dans votre navigateur, pas sur le serveur |
.css, polices et le reste | 40 | la mise en forme, et les fichiers joints aux articles |
| Exécutés sur le serveur | 0 |
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 sortie | WordPress | Site statique |
|---|---|---|
| connexion et devinette de mots de passe | wp-login.php, XML-RPC | aucun |
| interface d’administration | /wp-admin/, avec un éditeur de code | aucune |
| API programmable | REST et XML-RPC, avec écritures | aucune |
| code côté serveur à chaque requête | le cœur, le thème, chaque extension | aucun |
| base de données | oui, joignable depuis tout ce qui précède | aucune |
| stockage modifiable sur le serveur | les téléversements, et dans l’image d’origine, tout | aucun |
| tâches planifiées déclenchées par les visiteurs | wp-cron.php au chargement de la page | aucune |
| code tiers à maintenir à jour | chaque extension et chaque thème, sur le serveur | les outils de build, hors du serveur |
| ce qu’une session volée rapporte à un attaquant | le site | rien 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 Hugo | Ce qu’il empêche |
|---|---|
| « No arbitrary commands »19 | un template ne peut pas lancer de commande sur la machine |
exec.allow limité à Sass, Go, Git, Node et PostCSS20 | seulement des outils nommés, avec des options fixes |
funcs.getenv limité à ^HUGO_ et ^CI$20 | les templates ne peuvent pas lire vos secrets dans l’environnement |
| les requêtes distantes refusent les hôtes en adresse IP et localhost20 | un template ne peut pas atteindre votre propre réseau |
modules vérifiés contre go.sum19 | une 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.
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.
| Faille | Pourquoi 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 Cloudflare | un binaire substitué construirait ce qu’il voudrait | corrigé, 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 build | un changement en amont peut modifier le site en ligne sans aucun commit ici | ouvert, 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.
| Étape | Pourquoi |
|---|---|
| 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.sum | le build ne change que quand vous le changez |
construire dans un répertoire de sortie propre, sans --buildDrafts | ce que vous publiez est ce que vous vouliez publier |
| chercher tout ce qui est exécutable dans la sortie avant qu’elle parte | un 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éploiement | l’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 statiques | il 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 pages | WordPress de base 7.1.3 | Ce site sur nginx |
|---|---|---|
| Temps CPU, trois passes | 409 s, 530 s, 575 s | 0,15 s, 0,18 s, 0,19 s |
| Temps CPU par page | 41 à 57 ms | 15 à 19 µs |
| Pages par seconde | 168 à 236 | 23 679 à 28 044 |
| Mémoire sous charge | 302 à 310 Mio, plus 145 Mio pour la base | 15 Mio |
| Requêtes en échec | 0 | 0 |
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émoire | WordPress de base | Ce site |
|---|---|---|
| Images de conteneur pour le faire tourner | 803 Mo WordPress, 334 Mo MariaDB | 166 Mo nginx, ou rien du tout sur Cloudflare |
| Le site lui-même | 115 Mo de racine web, pour un site vide | 114 Mo, cinq langues, chaque article et chaque diagramme |
| Base de données | 163 Mo de fichiers pour 0,72 Mo de données | aucune |
| Mémoire au repos | 170 Mio, plus 139 Mio pour la base | 13 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 abandonnez | Ce que fait ce site à la place |
|---|---|
| les commentaires | aucun ; les lecteurs peuvent écrire par e-mail |
| la recherche côté serveur | un index JSON construit avec le site, interrogé dans votre navigateur |
| les formulaires qui envoient au serveur | aucun |
| les connexions et les espaces membres | aucun ; tout ici est public |
| l’édition dans un navigateur | les articles sont des fichiers texte dans un dépôt git |
| la publication instantanée sans build | un 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.
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 ». ↩︎
WordPress 4.7 release notes — « WordPress 4.7 comes with REST API endpoints for posts, comments, terms, users, meta, and settings. » ↩︎
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. » ↩︎
WordPress 3.5 release notes — « Enable XML-RPC by default, remove Writing Settings option ». ↩︎
WordPress REST API Handbook — « content that is public on your site is generally publicly accessible via the REST API ». ↩︎
WordPress REST API reference — Users —
GET /wp/v2/users, contexte par défaut « view » ; name, slug, link et avatar_urls dans le contexte view. ↩︎WordPress — Brute force attacks — « xmlrpc.php is a frequent brute‑force target » ; « Apply rate limits to /wp-login.php and /xmlrpc.php globally. » ↩︎
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. » ↩︎
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. » ↩︎
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. ↩︎ ↩︎
Cloudflare — Wrangler configuration — « The main key is optional for assets-only Workers. » ↩︎
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. » ↩︎
Cloudflare — Static assets billing and limitations — « Requests to static assets are free and unlimited. » ↩︎
Cloudflare — Static assets headers — en-têtes définis par « a plain text file called _headers ». ↩︎
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 ». ↩︎ ↩︎
NCSC — Making compromise difficult, 2.2 Reduce attack surface — « If would-be attackers can’t reach an interface, they can’t attack it. » ↩︎
NCSC — Cyber Essentials : Requirements for IT infrastructure v3.2 — « remove or disable unnecessary software (including applications, system utilities and network services) ». ↩︎
Hugo — Introduction — « Hugo is a static site generator written in Go, optimized for speed and designed for flexibility. » ↩︎
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. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Hugo — Configure security — « By default, access is restricted » ; listes d’autorisation par défaut pour exec, getenv et http. ↩︎ ↩︎ ↩︎ ↩︎
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 ». ↩︎
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. ↩︎ ↩︎