Come funziona questo controllo
Scanaris richiede due file nella radice del tuo dominio: /package.json, il manifesto di un progetto Node, e /composer.json, quello di un progetto PHP. Per ciascuno, conferma il rilievo solo se risponde 200, il corpo non è HTML, è JSON valido e contiene le chiavi proprie del manifesto: dependencies o devDependencies nel primo, require o require-dev nel secondo. Così non segnala una single-page app che restituisce il suo HTML per qualsiasi percorso, né un JSON qualunque che per caso vive lì. Ogni file trovato apre un rilievo di gravità bassa.
Perché è importante
Questi file non contengono segreti, ma contengono qualcosa di quasi altrettanto utile per un attaccante: la lista esatta delle librerie che usi e le loro versioni. Con quella non deve indovinare nulla: incrocia la tua lista con il database delle vulnerabilità note e sa quale exploit provare e contro cosa. Rivelano anche il nome interno del progetto, gli script di build e deploy, a volte l'URL del repository privato e i nomi dei pacchetti interni della tua organizzazione, che sono il punto di partenza degli attacchi di dependency confusion. È gravità bassa perché è informazione, non accesso; ma è informazione che regali senza motivo.
Perché succede
Perché il server web serve l'intera cartella del progetto invece della cartella di output. Un Vite, Nuxt o Next.js deployati bene pubblicano solo dist/, .output/public/ o .next/static/, e lì non c'è package.json. Compare quando si carica il repository completo via FTP in public_html, quando la directory radice di un hosting statico punta alla radice del progetto, o in progetti PHP dove composer.json sta nella stessa directory di index.php, cosa abituale in WordPress con Composer e nelle installazioni fatte a mano.
Come risolvere
- Servi solo la cartella pubblica. Su Vercel e Netlify, controlla l'"output directory" o "publish directory" del progetto; deve essere la cartella di build, non
.. In nginx e Apache, ilrootoDocumentRootpunta apublic/odist/. - In PHP, sposta
composer.json,composer.lockevendor/un livello sopra la directory pubblica se il tuo framework lo permette (Laravel e Symfony lo fanno già). Se non puoi spostarli, bloccali:
location ~* /(package|composer)(-lock)?\.json$ { return 404; }
location ~* /(yarn|pnpm-lock)\.(lock|yaml)$ { return 404; }
- In Apache, un
<FilesMatch "^(package|composer)(-lock)?\.json$"> Require all denied </FilesMatch>nella configurazione o nel.htaccess. - Controlla cos'altro è stato caricato. Dove c'è un
package.jsonpubblico di solito c'è un.env, un.git/o uncomposer.lockaccanto; il controllo file sensibili cerca quelli gravi.
Verifica che sia risolto
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://tuodominio.it/$p"
done
Atteso: 404 o 403. Un 200 con text/html è l'HTML generico di una single-page app e non è un problema; un 200 con application/json è il file vero.
Domande frequenti
Il mio repository è pubblico su GitHub comunque.
Allora il manifesto è già pubblico e questo rilievo non aggiunge nuova esposizione; puoi darlo per accettato. Comunque, correggere la radice del server evita che il prossimo file che finisce nella cartella del progetto venga pubblicato per sbaglio.
E i lockfile?
Questo controllo guarda i due manifesti, ma package-lock.json, yarn.lock e composer.lock sono peggiori: elencano tutte le dipendenze transitive con versione esatta. Se il manifesto è esposto, quasi certamente lo è anche il lockfile; la regola di blocco qui sopra li copre.
Se non servo il manifesto, nessuno può sapere che versioni uso?
Possono dedurlo per altre vie, più lente: header, percorsi dei bundle, comportamento. Ciò che librerie obsolete rileva nel tuo JavaScript, per esempio, lo vede chiunque. Nascondere il manifesto toglie la scorciatoia; aggiornare le dipendenze toglie il problema.