Como funciona esta verificação
O Scanaris pede dois ficheiros na raiz do teu domínio: /package.json, o manifesto de um projeto Node, e /composer.json, o de um projeto PHP. Para cada um, dá o achado por confirmado só se responder 200, o corpo não for HTML, for JSON válido e contiver as chaves próprias do manifesto: dependencies ou devDependencies no primeiro, require ou require-dev no segundo. Assim não marca uma aplicação de página única que devolve o seu HTML para qualquer rota, nem um JSON qualquer que por acaso viva ali. Cada ficheiro encontrado abre um achado de severidade baixa.
Porque importa
Estes ficheiros não contêm segredos, mas contêm algo quase tão útil para um atacante: a lista exata de bibliotecas que usas e as suas versões. Com isso não tem de adivinhar nada: cruza a tua lista com a base de dados de vulnerabilidades conhecidas e sabe que exploit testar e contra o quê. Também revelam o nome interno do projeto, os scripts de build e deploy, por vezes o URL do repositório privado e os nomes de pacotes internos da tua organização, que são o ponto de partida dos ataques de confusão de dependências. É severidade baixa porque é informação, não acesso; mas é informação que ofereces sem motivo.
Porque acontece
Porque o servidor web serve a pasta inteira do projeto em vez da pasta de saída. Um Vite, Nuxt ou Next.js bem implantados só publicam dist/, .output/public/ ou .next/static/, e aí não há package.json. Aparece quando se envia o repositório completo por FTP para public_html, quando o diretório raiz de um alojamento estático aponta para a raiz do projeto, ou em projetos PHP onde o composer.json está no mesmo diretório que o index.php, o que é habitual no WordPress com Composer e em instalações feitas à mão.
Como corrigir
- Serve só a pasta pública. No Vercel e no Netlify, revê o "output directory" ou "publish directory" do projeto; deve ser a pasta de build, não
.. No nginx e no Apache, orootouDocumentRootaponta parapublic/oudist/. - Em PHP, move o
composer.json, ocomposer.locke avendor/um nível acima do diretório público se a tua framework o permitir (o Laravel e o Symfony já o fazem). Se não os puderes mover, bloqueia-os:
location ~* /(package|composer)(-lock)?\.json$ { return 404; }
location ~* /(yarn|pnpm-lock)\.(lock|yaml)$ { return 404; }
- No Apache, um
<FilesMatch "^(package|composer)(-lock)?\.json$"> Require all denied </FilesMatch>na configuração ou no.htaccess. - Revê o que mais foi enviado. Onde há um
package.jsonpúblico costuma haver um.env, um.git/ou umcomposer.lockao lado; a verificação de ficheiros sensíveis procura os graves.
Confirma que está corrigido
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://teudominio.com/$p"
done
Esperado: 404 ou 403. Um 200 com text/html é o HTML genérico de uma aplicação de página única e não é problema; um 200 com application/json é o ficheiro real.
Perguntas frequentes
O meu repositório é público no GitHub de qualquer forma.
Então o manifesto já é público e este achado não acrescenta exposição nova; podes dá-lo por aceite. Mesmo assim, corrigir a raiz do servidor evita que o próximo ficheiro que caia na pasta do projeto seja publicado sem querer.
E os lockfiles?
Esta verificação olha para os dois manifestos, mas package-lock.json, yarn.lock e composer.lock são piores: listam todas as dependências transitivas com versão exata. Se o manifesto está exposto, quase de certeza que o lockfile também; a regra de bloqueio de cima cobre-os.
Se não servir o manifesto, ninguém sabe que versões uso?
Podem deduzi-lo por outros meios, mais lentos: cabeçalhos, caminhos dos bundles, comportamento. O que bibliotecas desatualizadas deteta no teu JavaScript, por exemplo, vê-o qualquer um. Esconder o manifesto tira o atalho; atualizar as dependências tira o problema.