Come funziona questo controllo
Scanaris analizza l'HTML della tua pagina principale e scorre tutti gli elementi con attributo src, href o action: script, immagini, fogli di stile, link, moduli. Per ogni valore verifica se punta a un host che non dovrebbe esistere fuori dalla tua macchina o dalla tua rete: localhost, 127.0.0.1, 0.0.0.0, gli IP privati 10.x.x.x, 192.168.x.x e da 172.16.x.x a 172.31.x.x, e qualsiasi dominio che finisce in .local. Se ne trova, apre un rilievo di gravità bassa con i primi riferimenti. Viene esaminato solo l'HTML iniziale; ciò che il JavaScript costruisce dopo non si vede per questa via.
Perché è importante
Un riferimento a http://localhost:3000/api/... in produzione ha due conseguenze. La prima è funzionale: per il tuo utente, localhost è il suo computer, quindi quell'immagine non carica, quel modulo viene inviato nel nulla e quello script fallisce in silenzio. La seconda è informativa: stai raccontando a chiunque che la tua API gira sulla porta 3000, che il tuo backend interno si chiama api.azienda.local o che la tua rete usa l'intervallo 10.0.1.x. Niente di tutto questo è una falla sfruttabile da sola, da qui la gravità bassa, ma sono pezzi che un attaccante conserva per dopo, e inoltre rivelano che il deploy è uscito senza revisione.
Perché succede
Quasi sempre è un URL scritto a mano durante lo sviluppo che nessuno ha cambiato. L'IA che ha generato il progetto ha messo http://localhost:8000 nel fetch del modulo, o nel src di un'immagine di prova, e in locale funzionava, quindi è rimasto. Altre cause: un .env di sviluppo finito nel build di produzione, un WordPress migrato da un ambiente locale con l'URL del sito non aggiornato, o un action di modulo copiato da un esempio.
Come risolvere
- Togli gli URL dal codice. La base della tua API va in una variabile d'ambiente (
NUXT_PUBLIC_API_BASE,NEXT_PUBLIC_API_URL,VITE_API_URL) con un valore diverso per ambiente. In produzione, la piattaforma (Vercel, Netlify, Railway) la inietta nel build. - Usa percorsi relativi ogni volta che la risorsa vive sul tuo stesso dominio:
/api/contattoinvece dihttp://localhost:3000/api/contatto. Funzionano uguale in locale e in produzione. - WordPress: aggiorna gli URL del sito nelle Impostazioni generali e sostituisci gli URL vecchi nel database con uno strumento che rispetti la serializzazione.
- Cerca nel build prima del deploy. Un passo di CI che fallisca se l'output compilato contiene questi host taglia il problema alla radice:
grep -rEl "localhost|127\.0\.0\.1|192\.168\.|\.local\b" dist/ .output/public/ 2>/dev/null
Se esce qualche file, c'è un riferimento da sistemare prima di pubblicare.
Verifica che sia risolto
curl -s https://tuodominio.it | grep -oE '(src|href|action)="https?://[^"]+"' | grep -iE "localhost|127\.0\.0\.1|0\.0\.0\.0|10\.[0-9]+\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|\.local\b"
Non deve uscire nulla. Prova anche una o due pagine interne, soprattutto quelle con moduli.
Domande frequenti
È un'immagine di prova che nessuno vede. Importa?
Il browser di ogni visitatore prova a caricarla dal proprio computer, e l'HTML continua ad annunciare il tuo host interno. Cambiala o cancellala; è un minuto.
Viene controllato anche il JavaScript compilato?
Non da questo controllo, solo l'HTML. Ma un URL interno nel bundle di solito è accompagnato da altre cose che non dovrebbero stare lì; segreti nel JavaScript cerca chiavi e token, e commenti HTML cerca residui nell'HTML stesso. I tre insieme coprono ciò che di solito resta di un ambiente di sviluppo.
Il mio link punta a un dominio .local apposta.
I domini .local sono riservati alle reti locali (mDNS) e non si risolvono su internet. Se vuoi collegare uno strumento interno, usa un sottodominio reale con autenticazione. Ciò che la tua app fa trapelare senza volerlo, con altri esempi, è in segreti che la tua app fa trapelare nel browser.