CORS: sicuro di conoscerlo?
CORS: è colpa del frontend o del backend? Cerchiamo di capire come funziona
CORS significa cross-origin HTTP request ed non è altro che una chiamata http effettuata attraverso browser da dominio-di-provenienza (es: www.dominio-di-provenienza.it) a dominio-di-destinazione (es: www.dominio-di-destinazione.it).
Un problema su cui tutti ci siamo schiantati almeno una volta è probabilmente questo:
Access to XMLHttpRequest at 'www.dominio-di-destinazione.it' from origin 'www.dominio-di-provenienza.it' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
Il motivo per cui incappiamo nel suddetto problema è che il server di dominio-di-destinazione non ha abilitato l’accesso al dominio-di-provenienza.
La cosa che spesso manda in confusione sviluppatore backend e sviluppatore frontend sta nel mezzo: il browser.
Perché?
Il browser per ragioni di sicurezza limita le richieste CORS e si accerta che sito di destinazione sia al corrente che la richiesta del sito di provenienza sia consentita.
Fatta questa premessa (ampiamente documentata su MDN) vorrei mostrare un esempio molto semplice di chiamata CORS che non va a buon fine.
Per rendere l’esempio di brutale immediatezza, mostrerò direttamente in console (sul dominio di questo blog) il codice per effettuare una chiamata Ajax costruita con Postman verso la pagina mdn che spiega proprio cos’è CORS.
La parte migliore che sfugge a... un sacco di gente
Quando si effettua una richiesta CORS, come nel caso mostrato nella gif di prima, parte una chiamata con metodo OPTIONS. Questa chiamata viene detta CORS preflight.
Di seguito il flusso di una CORS request con preflight.
Quello che non molti sanno è che se la richiesta soddisfa determinati requisiti la CORS preflight non è necessario che venga effettuata:
Metodi permessi:
GET
HEAD
POST
Header permessi:
Accept
Accept-Language
Content-Language
Content-Type con uno dei seguenti valori:
application/x-www-form-urlencoded
multipart/form-data
text/plain
DPR
Downlink
Save-Data
Viewport-Width
Width
Sarà vero? Provo!
Quando vado a visualizzare l’header non è presente alcun preflight. C’è la POST request passa diretta. Nessun metodo OPTIONS.
Access-Control-Allow-Origin
Come abbiamo scritto in precedenza, la richiesta partita è consentita solo se dominio-di-provenienza la consente, altrimenti restituirà errore.
Per essere consentita, il browser verifica che nell’header di risposta sia presente la chiave ”Access-Control-Allow-Origin
” con valore “dominio-di-provenienza” o, come spesso avrete visto, ”*” (ossia da qualsiasi provenienza).
Quindi lato backend che faccio?
Non è raro trovare soluzioni raffazzonate come applicare negli header del dominio-di-destinazione:
Access-Control-Allow-Origin: *
Fino a quando si tratta di simple request può andare bene, ma come come comportarsi quando ad arrivare sono richieste con metodo OPTIONS?
L’ideale sarebbe intercettare tutte le richieste OPTIONS e ritornare una risposta contenente i seguenti header:
HTTP STATUS 200Access-Control-Allow-Origin: *Access-Control-Allow-Methods: POST, GET, OPTIONS,Access-Control-Allow-Headers: Content-Type
Ma è un soluzione artigianale, generalmente i framework più popolari hanno delle configurazioni già built-in che facilitano lo sviluppatore nel compito, possibilmente scrivendo anche una sola riga di codice.
Considerazioni personali
Io credo che tutti, veramente tutti, debbano conoscere CORS, frontender e backender. Per questo vi suggerisco vivamente di leggere l’articolo presente su MDN molto più tecnico ed accurato.
Fonti e approfondimenti
Italian coders - che ringrazio per gli spunti che spesso offrono nei loro articoli