CORS: cos'è e perché il browser blocca un'API | POLPROG Vai al contenuto

CORS: cos'è e perché il browser blocca un'API

CORS non è un meccanismo che blocca semplicemente le connessioni a un'API. È un protocollo del browser costruito sulla same-origin policy che decide se JavaScript proveniente da un origin può leggere la risposta di un altro origin. In alcuni casi la richiesta HTTP viene inviata e il browser blocca solo l'accesso dello script alla risposta. In altri casi invia prima un preflight OPTIONS e usa il risultato per decidere se la richiesta reale può essere inviata.

Pubblicato Scritto da Tempo di lettura 19 min di lettura

CORS non è un meccanismo che blocca semplicemente le connessioni a un'API. È un protocollo del browser costruito sulla same-origin policy che decide se JavaScript proveniente da un origin può leggere la risposta di un altro origin. In alcuni casi la richiesta HTTP viene inviata e il browser blocca solo l'accesso dello script alla risposta. In altri casi invia prima un preflight OPTIONS e usa il risultato per decidere se la richiesta reale può essere inviata.

In questa pagina
  1. 1CORS parte dalla same-origin policy
  2. 2Che cosa significa esattamente origin
  3. 3Cosa significa davvero quando il browser blocca l'API
  4. 4Richieste semplici: quando il browser evita il preflight
  5. 5Preflight OPTIONS: cosa controlla il browser
  6. 6Gli headers CORS più importanti
  7. 7`Access-Control-Allow-Origin`: wildcard o origin esplicito
  8. 8Cookie e credentials: fonte comune di problemi
  9. 9Perché `mode: "no-cors"` di solito non risolve nulla
  10. 10Perché curl, Postman o backend possono funzionare
  11. 11CORS non sostituisce CSRF o autorizzazione
  12. 12Errori comuni e diagnosi
  13. 13Configurazioni CORS pericolose
  14. 14Prestazioni: costo del preflight e `Access-Control-Max-Age`
  15. 15Configurazione pratica e checklist di deploy

CORS parte dalla same-origin policy

La same-origin policy è un meccanismo di sicurezza fondamentale del browser. Limita la capacità di un documento o script di leggere dati provenienti da un altro origin e impedisce, tra le altre cose, che un sito malevolo legga dati da un servizio in cui l'utente è autenticato. [3]

CORS, Cross-Origin Resource Sharing, è un meccanismo HTTP che permette al server di rilassare questa regola e dichiarare esplicitamente quali origins possono leggere una risposta. Il Fetch Standard lo descrive come protocollo opt-in. [1][2]

Che cosa significa esattamente origin

PaginaURL targetRelazionePerché
https://app.example.comhttps://app.example.com/apiStesso originStesso schema, host e porta
https://app.example.comhttps://api.example.comOrigin diversoHost diverso
https://app.example.comhttp://app.example.comOrigin diversoSchema diverso
https://app.example.comhttps://app.example.com:8443Origin diversoPorta diversa
https://app.example.comhttps://app.example.com/v2Stesso originCambia solo il path

Un origin è definito da schema, host e porta. Due URL sono same-origin solo se tutti e tre coincidono. Il path non conta. [3][4]

`https://app.example.com` e `https://api.example.com` sono quindi origins diversi. Lo stesso vale per HTTP contro HTTPS o porte differenti. [4]

Same-origin e same-site non sono equivalenti. È importante soprattutto con i cookie, perché `SameSite` usa il concetto di site mentre CORS usa origin. [13]

Cosa significa davvero quando il browser blocca l'API

Dire che il browser blocca la richiesta è spesso troppo generico. Per richieste cross-origin semplici può inviare la richiesta, ricevere una risposta HTTP valida e poi rifiutare di esporla a JavaScript se il controllo CORS fallisce. [1][6]

Per richieste con preflight il flusso è diverso. Il browser invia prima `OPTIONS`. Se il preflight fallisce, la richiesta reale non viene inviata. [2][5][6]

Questa differenza è importante con operazioni che modificano dati. Un errore CORS non significa automaticamente che il backend non abbia ricevuto alcuna richiesta.

Richieste semplici: quando il browser evita il preflight

MetodoHeaders di esempioRichiesta sempliceEffetto
GETAcceptNessun preflight
POSTContent-Type: text/plainNessun preflight
POSTContent-Type: application/jsonNoJSON causa preflight
PUTContent-Type: application/jsonNoIl metodo causa preflight
GETAuthorization: Bearer ...NoL'header causa preflight

Una richiesta è semplice solo se rispetta la CORS safelist. I metodi consentiti sono `GET`, `HEAD` e `POST`. Gli headers impostati manualmente devono essere safelisted e `Content-Type` può usare solo `application/x-www-form-urlencoded`, `multipart/form-data` o `text/plain`. [1]

Un normale `POST` con `Content-Type: application/json` non è semplice e in genere causa preflight. Lo stesso vale per `PUT`, `DELETE` o headers personalizzati come `X-Request-ID`. [1][11]

Preflight OPTIONS: cosa controlla il browser

Il preflight è una richiesta `OPTIONS` automatica. Il browser invia `Origin`, `Access-Control-Request-Method` e, se necessario, `Access-Control-Request-Headers`. Il server risponde con origins, metodi e headers consentiti. [2][5]

Se l'app vuole inviare `DELETE` con `Authorization`, il browser può prima verificare se quell'origin può usare metodo e header. Solo una risposta positiva permette la richiesta reale. [5]

Il preflight aggiunge un round trip, ma il risultato può essere memorizzato in una cache CORS dedicata, separata dalla cache HTTP normale. [5][10]

Gli headers CORS più importanti

HeaderDirezioneRuolo
OriginRichiestaOrigin che avvia la richiesta
Access-Control-Allow-OriginRispostaOrigin il cui codice può ricevere la risposta
Access-Control-Allow-MethodsRisposta preflightMetodi consentiti dal preflight
Access-Control-Allow-HeadersRisposta preflightRequest headers consentiti dal preflight
Access-Control-Allow-CredentialsRispostaPermesso di esporre una risposta con credentials
Access-Control-Expose-HeadersRispostaResponse headers aggiuntivi visibili a JavaScript
Access-Control-Max-AgeRisposta preflightDurata cache del risultato preflight
Vary: OriginRispostaIndica alla cache che la risposta varia per Origin

`Origin` identifica l'origin che avvia la richiesta. `Access-Control-Allow-Origin` è un header di risposta che dice al browser se la risposta può essere condivisa con codice di quell'origin. [1][2][8]

`Access-Control-Allow-Methods` e `Access-Control-Allow-Headers` sono particolarmente importanti nel preflight. `Access-Control-Allow-Credentials` permette di esporre una risposta con credentials, mentre `Access-Control-Expose-Headers` rende disponibili headers aggiuntivi a JavaScript. [1][2]

Se il server restituisce dinamicamente `Access-Control-Allow-Origin`, MDN raccomanda anche `Vary: Origin`. [1]

`Access-Control-Allow-Origin`: wildcard o origin esplicito

`Access-Control-Allow-Origin: *` permette di condividere una risposta con qualsiasi origin per richieste senza credentials. Può essere corretto per un'API realmente pubblica. [2][8]

Per endpoints privati o sensibili l'insieme consentito dovrebbe essere minimo. MDN e OWASP raccomandano origins specifici quando non serve accesso globale. [14][17]

Con più origins consentiti, il server deve confrontare l'`Origin` con una allowlist e restituire solo quello validato. Non deve riflettere ciecamente qualsiasi origin. [8][16]

Fetch può usare `credentials: "include"` per includere credentials nelle richieste cross-origin. Non basta. Il server deve restituire `Access-Control-Allow-Credentials: true` e un `Access-Control-Allow-Origin` esplicito. `*` non è ammesso in questo caso. [2][6][9]

I cookie seguono inoltre regole proprie. `SameSite` può impedire l'invio del cookie in una richiesta cross-site e `SameSite=None` richiede `Secure`. [13]

CORS e `SameSite` risolvono problemi diversi. CORS può essere corretto ma il cookie non essere inviato, oppure il cookie può essere inviato e CORS continuare a bloccare JavaScript.

Perché `mode: "no-cors"` di solito non risolve nulla

`fetch(..., { mode: "no-cors" })` non aggira CORS quando l'applicazione deve leggere la risposta. Metodi e headers sono limitati e la risposta è opaque. JavaScript non può leggere body o headers e lo status esposto è `0`. [6][7][11]

`no-cors` ha usi specializzati, inclusi alcuni casi con Service Worker, ma normalmente è sbagliato per una classica API JSON. [6]

Se non controlli l'API esterna e non offre CORS, un backend o proxy sotto il tuo controllo può fare la richiesta server-to-server. [11][12]

Perché curl, Postman o backend possono funzionare

Same-origin policy e CORS sono applicati dal browser. Un client HTTP fuori da quel modello non è soggetto allo stesso controllo. Per questo un endpoint può funzionare con curl o un tool API ma fallire da JavaScript in pagina. [2][3][16]

Questo dimostra anche che CORS non è autenticazione API. Un client non-browser può scegliere il proprio `Origin`, quindi OWASP sconsiglia di usarlo come prova di identità. [16]

L'API necessita comunque di autorizzazione, token, sessioni, controlli dei permessi e validazione server-side.

CORS non sostituisce CSRF o autorizzazione

La same-origin policy limita soprattutto le letture cross-origin. Form e alcune richieste semplici possono comunque essere inviate tra siti, quindi le applicazioni basate su cookie necessitano ancora di protezioni CSRF. [3][15]

OWASP raccomanda esplicitamente di non usare solo CORS o `Origin` come controllo di accesso a risorse sensibili. Autenticazione e autorizzazione restano necessarie. [16]

CORS risponde alla domanda se il browser può esporre una risposta al codice di un origin, non se l'utente o client ha diritto di eseguire l'operazione.

Errori comuni e diagnosi

La mancanza di `Access-Control-Allow-Origin` è un errore frequente. Altre cause sono metodo non consentito, headers mancanti in `Access-Control-Allow-Headers`, `*` con credentials, risposta OPTIONS errata o redirect nel flusso CORS. [11][12]

JavaScript riceve volutamente pochi dettagli. Console e pannello Network di DevTools sono più utili. [1][11]

`CORS request did not succeed` può anche derivare da DNS, timeout, connessione rifiutata, TLS, mixed content o estensioni. [11]

  • Controlla l'origin esatto del frontend: schema, host e porta.
  • Ispeziona `OPTIONS` quando presente.
  • Verifica status e `Access-Control-Allow-*` del preflight.
  • Controlla che anche la risposta reale includa gli headers CORS richiesti.
  • Con cookie verifica `credentials`, `Access-Control-Allow-Credentials`, `SameSite` e `Secure`.
  • Controlla redirect, TLS, mixed content ed errori di rete.
  • Confronta la richiesta del browser con una richiesta funzionante da curl o tool API.

Configurazioni CORS pericolose

Riflettere senza validazione l'`Origin` ricevuto è pericoloso quando richieste con credentials restituiscono dati sensibili. Un attaccante può controllare un origin e tentare di leggere risposte nel contesto di un utente autenticato. [16]

Regex troppo larghe e fiducia in tutti i sottodomini sono rischiose se uno di essi può essere compromesso. OWASP raccomanda una allowlist con confronto preciso. [16][17]

`Access-Control-Allow-Origin: *` non è automaticamente una vulnerabilità per dati interamente pubblici senza credentials. Il problema è una policy più permissiva del necessario. [8][17]

Prestazioni: costo del preflight e `Access-Control-Max-Age`

Il preflight aggiunge una richiesta `OPTIONS` prima di quella reale e può aumentare la latenza. Il risultato può essere memorizzato con `Access-Control-Max-Age` in una cache separata da quella HTTP. [5][10]

MDN indica un valore predefinito di 5 secondi senza header. I browser applicano limiti propri. Firefox limita a 86400 secondi e Chromium dalla versione 76 a 7200 secondi. [10]

Conviene cacheare policy stabili, ma non tanto da rendere difficile revocare rapidamente una regola troppo ampia.

Configurazione pratica e checklist di deploy

Prima decidi se l'endpoint deve davvero essere accessibile cross-origin. Se no, non aggiungere headers CORS. Se sì, definisci allowlist di origins, metodi, headers e necessità di credentials. [14][17]

Per una allowlist dinamica restituisci solo un origin verificato e aggiungi `Vary: Origin`. Con credentials usa origin esplicito e `Access-Control-Allow-Credentials: true`. Il frontend non può sostituire questa decisione del server. [1][2]

Dopo il deploy prova una richiesta semplice, un preflight, un caso con cookie, un origin non consentito e gli errori reali in DevTools.

  • L'accesso cross-origin è davvero necessario?
  • Quali origins esatti devono essere consentiti?
  • Servono credentials?
  • Quali metodi e request headers sono necessari?
  • `OPTIONS` raggiunge l'applicazione e risponde correttamente?
  • La risposta reale contiene gli headers CORS necessari?
  • Le risposte dinamiche includono `Vary: Origin`?
  • L'endpoint richiede ancora autenticazione e autorizzazione normali?
  • La policy è stata testata con origin consentito e non consentito?

CORS va considerato una regola del browser per l'accesso alle risposte, non una barriera universale di sicurezza per un'API. Prima identifica gli origins di frontend e API, poi determina se la richiesta è semplice o richiede preflight e infine verifica OPTIONS e gli headers CORS della risposta reale. Una configurazione sicura consente solo origins, metodi e headers necessari, lasciando autenticazione e autorizzazione al livello applicativo.

CORS Same-Origin Policy Web Security HTTP API Fetch API Preflight Cookies CSRF Frontend

Domande frequenti

Cos'è CORS?

CORS è un protocollo HTTP usato dai browser per controllare la condivisione delle risposte tra origins diversi. Il server indica con headers quali origins possono leggere una risposta. [1][2]

Perché l'API funziona in Postman ma non nel browser?

Perché CORS è applicato dal browser come parte della same-origin policy. I client HTTP esterni non sono soggetti allo stesso controllo. [2][3][16]

CORS blocca sempre l'invio della richiesta?

No. Una richiesta semplice può essere inviata e il browser può soltanto negare a JavaScript l'accesso alla risposta. Se fallisce un preflight richiesto, la richiesta reale non viene inviata. [1][5][6]

Cosa provoca un preflight?

Tra gli esempi: metodi diversi da GET, HEAD, POST, headers personalizzati e Content-Type: application/json. Il browser invia prima OPTIONS. [1][5]

Access-Control-Allow-Origin va aggiunto nel frontend?

No. È un header di risposta del server. Il frontend non può concedersi da solo il permesso. [1][8][12]

mode: "no-cors" risolve CORS?

Non per una normale API da leggere. La risposta diventa opaque e body, headers e status normale non sono accessibili. [6][7]

Si può usare Access-Control-Allow-Origin: *?

Sì per risposte pubbliche senza credentials. Non per esporre risposte con credentials. [2][8][9]

Come funziona CORS con i cookie?

Il frontend usa spesso credentials: "include", mentre il server deve restituire origin esplicito e Access-Control-Allow-Credentials: true. Il cookie resta soggetto a SameSite e Secure. [6][9][13]

CORS protegge l'API da curl o bot?

No. CORS limita l'accesso JavaScript nel browser. Un client esterno può inviare una propria richiesta e un proprio Origin, quindi serve autorizzazione. [16]

CORS protegge da CSRF?

Non completamente. Le applicazioni basate su cookie hanno ancora bisogno di difese CSRF. [3][15][16]

A cosa serve Vary: Origin?

Quando Access-Control-Allow-Origin varia dinamicamente, Vary: Origin informa le cache che la risposta dipende dall'origin richiedente. [1]

Come ridurre i preflight?

Si può cacheare il risultato con Access-Control-Max-Age o usare richieste semplici quando è appropriato dal punto di vista funzionale e della sicurezza. [1][10][11]

Fonti e riferimenti

  1. MDN, Cross-Origin Resource Sharing (CORS)123456789101112131415
  2. WHATWG, Fetch Standard, CORS protocol123456789101112
  3. MDN, Same-origin policy123456
  4. MDN, Origin12
  5. MDN, Preflight request1234567
  6. MDN, Using the Fetch API12345678
  7. MDN, RequestInit12
  8. MDN, Access-Control-Allow-Origin123456
  9. MDN, Access-Control-Allow-Credentials123
  10. MDN, Access-Control-Max-Age1234
  11. MDN, CORS errors1234567
  12. MDN, CORS header Access-Control-Allow-Origin missing123
  13. MDN, Set-Cookie123
  14. MDN, Practical CORS configuration12
  15. MDN, Cross-site request forgery (CSRF)12
  16. OWASP, HTML5 Security Cheat Sheet, Cross Origin Resource Sharing123456789
  17. OWASP, HTTP Headers Cheat Sheet, Access-Control-Allow-Origin1234

È stato utile?

Ricevi i nuovi articoli via e-mail

Una breve e-mail per ogni nuovo articolo di Formazione. Niente spam, disiscriviti con un clic.

Usiamo la tua e-mail solo per inviare nuovi articoli. Nessuna condivisione con terze parti.

Torna alla Formazione