Come funziona questo controllo
Scanaris richiede di nuovo la tua pagina principale con un header Origin di un dominio inventato, come farebbe il browser se un altro sito provasse a chiamarti, e guarda cosa risponde il tuo server. Se Access-Control-Allow-Origin restituisce quella stessa origine e in più Access-Control-Allow-Credentials vale true, apre un riscontro di severità alta: il tuo server riflette qualsiasi origine con credenziali. Se risponde con il wildcard * insieme alle credenziali, ne apre uno di severità bassa: i browser rifiutano quella combinazione, ma tradisce una configurazione fatta alla cieca. Si prova solo l'URL analizzato; se la tua API vive su un'altra rotta o dominio, provala con il comando qui sotto.
Cosa fa un attaccante con questo
Mette su una pagina su qualsiasi dominio con un fetch verso la tua API usando credentials: 'include'. Quando uno dei tuoi utenti, con la sessione aperta, visita quella pagina, il browser allega i suoi cookie, il tuo server risponde con i dati dell'utente e, siccome la policy CORS lo permette, la pagina dell'attaccante li legge. Profilo, ordini, messaggi, tutto ciò che restituisce l'API. L'utente non vede nulla: avviene in background.
Perché succede
CORS esiste perché il browser blocchi di default le letture tra domini. Quando una chiamata fallisce in sviluppo (l'app su una porta, l'API su un'altra), la soluzione rapida suggerita da tutorial e strumenti di IA è "permettere tutte le origini", e perché i cookie funzionino si aggiunge "con credenziali". In produzione significa che la protezione è stata disattivata. Più dettagli in CORS configurato male: il difetto silenzioso che espone i tuoi utenti.
Come sistemarlo
- Lista esplicita di origini. Solo i domini che chiamano davvero la tua API dal browser. In Express:
cors({ origin: ['https://app.tuodominio.com'], credentials: true }). In Next.js, nel Route Handler, confronta l'Origincon la tua lista prima di restituirlo. In Nuxt, conrouteRulese l'opzionecorso un middleware server. - Non riflettere mai l'origine. Copiare il valore di
Originnella risposta "perché così funziona tutto" è esattamente l'errore che il controllo misura. - Se non ti servono cookie tra domini, togli
Access-Control-Allow-Credentials. Senza credenziali, un*su un'API pubblica in sola lettura è accettabile. - Aggiungi Vary: Origin quando la risposta dipende dall'origine, perché nessuna cache serva a un dominio la risposta di un altro.
- Cookie con SameSite=Lax o Strict come seconda barriera: anche se CORS fallisse, il browser non li invierebbe da un altro sito.
Verifica che sia sistemato
curl -sI -H "Origin: https://evil.example" https://tuodominio.com/api/me | grep -i "access-control"
Il risultato corretto è che Access-Control-Allow-Origin non compaia per quell'origine, o compaia con il tuo dominio e non con evil.example. Prova anche con il tuo dominio in Origin per confermare che la tua app continui a funzionare.
Domande frequenti
La mia API è pubblica e senza login. È un problema?
No, se non ci sono cookie né header di autorizzazione. Access-Control-Allow-Origin: * senza credenziali è la configurazione corretta per un'API aperta.
Ho vari sottodomini che chiamano l'API.
Elencali tutti o valida l'Origin contro un pattern stretto del tuo dominio radice. Non validare mai con "contiene tuodominio.com": tuodominio.com.evil.example lo supera.
E localhost per sviluppare?
Aggiungilo solo nell'ambiente di sviluppo, con una variabile d'ambiente. Un http://localhost:3000 nella lista di produzione è un'origine che qualsiasi programma sul computer dell'utente può usare.