Comment fonctionne cette vérification
Scanaris demande deux fichiers à la racine de ton domaine : /package.json, le manifeste d'un projet Node, et /composer.json, celui d'un projet PHP. Pour chacun, il ne confirme le constat que s'il répond 200, que le corps n'est pas du HTML, que c'est du JSON valide et qu'il contient les clés propres au manifeste : dependencies ou devDependencies pour le premier, require ou require-dev pour le second. Il ne signale donc ni une application monopage qui renvoie son HTML pour n'importe quel chemin, ni un JSON quelconque qui vivrait là par hasard. Chaque fichier trouvé ouvre un constat de sévérité faible.
Pourquoi c'est important
Ces fichiers ne contiennent pas de secrets, mais ils contiennent quelque chose de presque aussi utile à un attaquant : la liste exacte des bibliothèques que tu utilises et leurs versions. Avec ça, il n'a rien à deviner : il croise ta liste avec la base des vulnérabilités connues et sait quel exploit essayer et contre quoi. Ils révèlent aussi le nom interne du projet, les scripts de build et de déploiement, parfois l'URL du dépôt privé et les noms des paquets internes de ton organisation, qui sont le point de départ des attaques par confusion de dépendances. C'est de sévérité faible parce que c'est de l'information, pas un accès ; mais c'est de l'information que tu donnes sans raison.
D'où ça vient
Du serveur web qui sert tout le dossier du projet au lieu du dossier de sortie. Un Vite, Nuxt ou Next.js bien déployé ne publie que dist/, .output/public/ ou .next/static/, et il n'y a pas de package.json là-dedans. Ça apparaît quand le dépôt complet est envoyé par FTP dans public_html, quand le répertoire racine d'un hébergement statique pointe vers la racine du projet, ou dans des projets PHP où composer.json se trouve dans le même répertoire qu'index.php, ce qui est courant sous WordPress avec Composer et dans les installations faites à la main.
Comment corriger
- Ne sers que le dossier public. Sur Vercel et Netlify, vérifie l'« output directory » ou le « publish directory » du projet ; ce doit être le dossier de build, pas
.. Dans nginx et Apache, lerootou leDocumentRootpointe verspublic/oudist/. - En PHP, déplace
composer.json,composer.locketvendor/un niveau au-dessus du répertoire public si ton framework le permet (Laravel et Symfony le font déjà). Si tu ne peux pas les déplacer, bloque-les :
location ~* /(package|composer)(-lock)?\.json$ { return 404; }
location ~* /(yarn|pnpm-lock)\.(lock|yaml)$ { return 404; }
- Dans Apache, un
<FilesMatch "^(package|composer)(-lock)?\.json$"> Require all denied </FilesMatch>dans la configuration ou dans le.htaccess. - Vérifie ce qui a été envoyé d'autre. Là où il y a un
package.jsonpublic, il y a souvent un.env, un.git/ou uncomposer.lockà côté ; la vérification fichiers sensibles cherche les graves.
Vérifie que c'est corrigé
for p in package.json composer.json package-lock.json composer.lock yarn.lock; do
printf "%-20s " "$p"; curl -s -o /dev/null -w "%{http_code} %{content_type}\n" "https://tondomaine.com/$p"
done
Attendu : 404 ou 403. Un 200 en text/html est le HTML générique d'une application monopage et n'est pas un problème ; un 200 en application/json est le vrai fichier.
Questions fréquentes
Mon dépôt est public sur GitHub de toute façon.
Alors le manifeste est déjà public et ce constat n'ajoute aucune exposition nouvelle ; tu peux le marquer comme accepté. Malgré tout, corriger la racine du serveur évite que le prochain fichier qui tombe dans le dossier du projet soit publié sans le vouloir.
Et les lockfiles ?
Cette vérification regarde les deux manifestes, mais package-lock.json, yarn.lock et composer.lock sont pires : ils listent toutes les dépendances transitives avec leur version exacte. Si le manifeste est exposé, le lockfile l'est presque sûrement aussi ; la règle de blocage ci-dessus les couvre.
Si je ne sers pas le manifeste, personne ne peut savoir quelles versions j'utilise ?
On peut le déduire par d'autres moyens, plus lents : en-têtes, chemins des bundles, comportement. Ce que bibliothèques obsolètes détecte dans ton JavaScript, par exemple, n'importe qui le voit. Cacher le manifeste supprime le raccourci ; mettre à jour les dépendances supprime le problème.