Abbiamo già parlato di CORS in un articolo precedente. Ma cosa sono i CORS? I CORS (Cross-Origin Resource Sharing) sono un meccanismo di sicurezza che consente a una origine (ad esempio, un sito web) di accedere a risorse di un’altra origine (ad esempio, un’API esterna) tramite un browser. Un classico esempio potrebbe essere quello in cui dal nostro browser richiamiamo una pagina web del dominio AAA che al suo interno contiene un riferimento a una risorsa del dominio BBB. Se BBB non autorizza il dominio AAA, tramite configurazione CORS, l’utilizzo di quella risorsa, questa non verrà erogata.
I CORS servono quindi a “decidere” CHI, al di fuori del nostro dominio, può richiamare e utilizzare le nostre risorse e i nostri servizi.
Ciò è necessario poiché il browser utilizza la politica di same-origin per prevenire le richieste cross-origin non autorizzate. I server possono utilizzare gli header CORS per indicare quali origini possono accedere alle loro risorse.
Se per esempio sulla mia pagina pubblico un’immagine, posso scegliere quale sito, oltre al mio, può o meno accedere e utilizzare o pubblicare quella stessa immagine.
Indice dei contenuti
- Configurazione dei CORS
- Esempio di configurazione CORS su Apache
- Test configurazione CORS
- Header utilizzabili
- Errori CORS più comuni
- Contributo Video
Let’s go!
Configurazione dei CORS
La configurazione dei CORS può variare a seconda del framework o del server che si sta utilizzando. Di seguito alcuni esempi di come configurare i CORS in alcuni ambienti comuni:
Server Apache: È possibile utilizzare il modulo mod_headers per abilitare i CORS per tutte le richieste o per richieste specifiche. Esempio:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Su questo primo esempio approfondiremo più avanti in quanto non tutti browser accettano l'”*” e sarebbe più utile, oltre che più sicuro specificare nel dettaglio quali domini siano autorizzati a richiamare le nostre preziose risorse. Troveremo l’* anche negli esempi successivi. La premessa appena fatta resta valida per tutti.
Server Nginx: Anche qui, come su apache httpd, è possibile utilizzare il comando add_header per abilitare i CORS per tutte le richieste o per richieste specifiche. Esempio:
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
Questi esempi ovviamente vanno calati all’interno delle specifiche situazioni. Continuate a leggere e non limitatevi a copiarli e incollarli sul vostro server. Prima di tutto perchè potreste non risolvere il problema. Secondo, potreste ricevere in cambio più danni che soluzioni.
Esempio di configurazione CORS su Apache
Vediamo quali passi seguire per configurare i CORS sul websers Apache httpd.
Assicurati di avere il modulo mod_headers abilitato in Apache. Puoi verificarlo eseguendo il comando sudo apachectl -M e cercando headers_module nell’elenco dei moduli caricati. Se non è presente, puoi abilitarlo utilizzando il comando sudo a2enmod headers.
Apri il file di configurazione di Apache del tuo sito web. Il file di configurazione dipende dal sistema operativo che si sta utilizzando, ma solitamente si trova in /etc/apache2/sites-available/tuo-sito.conf. A meno che abbiate compilato voi stesso l’apache.
Aggiungi le seguenti righe per abilitare i CORS per tutte le richieste:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
Se vuoi limitare le origini consentite, puoi sostituire “*” con l’URL dell’origine consentita, ad esempio “example.com”. Puoi anche specificare più origini consentite separandole con una virgola, ad esempio: “esempio.com, esempio.org”.
Header set Access-Control-Allow-Origin "esempio.com, esempio.org"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
- Se vuoi limitare i metodi consentiti, puoi sostituire “GET, POST, PUT, DELETE, OPTIONS” con i metodi consentiti separati da una virgola. Ad esempio, se vuoi consentire solo le richieste GET: “Header set Access-Control-Allow-Methods “GET”:
Header set Access-Control-Allow-Origin "esempio.com, esempio.org"
Header set Access-Control-Allow-Methods "GET"
Header set Access-Control-Allow-Headers "Content-Type"
Se vuoi consentire solo determinati headers nella richiesta, puoi sostituire “Content-Type” con i headers consentiti separati da una virgola.
Salva il file e riavvia Apache utilizzando il comando sudo service apache2 restart o sudo systemctl restart apache2.
Ora i CORS dovrebbero essere configurati correttamente per il tuo sito web. Puoi testare la configurazione inviando una richiesta di prova da un sito web diverso e verificando che i header CORS corretti vengano restituiti dal server.
Configurazione definitiva
Quella che segue è la configurazione che prediligo che offre la possibilità di impostare gli FQDN abilitati in una variabile d’ambiente. In questo modo si aggirano anche le limitazioni già descritte nell’utilizzo dell’asterisco “*”.
SetEnvIf Origin "http(s)?://(.+\.)?(iegri.com|google.com|facebook.com|linkedin.com|twitter.com)$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Headers "authorization, clientversion, Cache-Control, pragma, expires, accept, BusinessID, InteractionDate-Date, InteractionDate-Time, SourceSystem, Channel, devicetype, content-type, x-requested-with"
in SetEnvIf Origin inserite la lista dei domini per i quali volete conserve l’utilizzo del risorse.
La riga che inizia con Header set Access-Control-Allow-Headers è un ulteriore esempio di header che si può inserire nella configurazione. La lista più esaustiva degli headers che si possono configurare è riportata più avanti.
Test configurazione CORS
Vediamo come eseguire test utili a verificare che le configurazioni apportate siano efficaci.
Crea una semplice pagina HTML con un form di prova sul tuo computer che invia una richiesta cross-origin (ovvero da un server a un altro) al tuo server. Quindi, per riepilogare, avremo una pagina sul tuo computer che prova a richiamare un’API sul sito che stai proteggendo con le configurazioni appena apportate.
<!DOCTYPE html>
<html>
<head>
<title>Test CORS</title>
</head>
<body>
<!-- Sostituisci https://example.com/api/test con il path corretto relativo al tuo sito -->
<form id="cors-form" method="post" action="https://example.com/api/test">
<input type="text" name="test-data" placeholder="Inserisci i dati di prova">
<button type="submit">Invia richiesta</button>
</form>
</body>
</html>
Apri la pagina HTML in un browser e invia la richiesta di prova cliccando il pulsante “Invia richiesta”.
Verifica che la richiesta sia stata inviata correttamente e che il server abbia restituito i header CORS corretti nella risposta. Utilizzando le funzionalità di debug del browser (ad esempio, la console di sviluppo) puoi verificare che i seguenti headers siano presenti nella risposta:
- Access-Control-Allow-Origin: deve essere impostato su “*” o sull’URL dell’origine da cui è stata inviata la richiesta.
- Access-Control-Allow-Methods: deve essere impostato sui metodi consentiti per le richieste cross-origin.
- Access-Control-Allow-Headers: deve essere impostato sui headers consentiti per le richieste cross-origin.
Se tutti i headers sono presenti e corretti, la configurazione dei CORS dovrebbe essere corretta. In caso contrario, dovrai verificare la configurazione del server e correggere eventuali errori.
Per farvi capire meglio, ho impostato questa configurazione CORS:
Header set Access-Control-Allow-Origin "iegri.com, facebook.com, twitter.com, linkedin.com, google.com"
Header set Access-Control-Allow-Methods "GET"
Header set Access-Control-Allow-Headers "Content-Type"
Richiamando la mia pagina, su cui queste configurazioni sono state apportate, ecco cosa appare nei response headers:
Negli headers di risposta possiamo vedere esattamente quello che abbiamo configurato.
Nel caso in cui il server chiamante non sia tra quelli abilitati, riceveremo un errore CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.:
Verificare l’implementazione della configurazione CORS tramite curl
Se state lavorando su una macchina linux e non avete accesso a un browser, ecco come testare le configurazioni, tramite il comando curl:
curl -X GET https://example.com/pagina-da-testare -X OPTIONS -H "Origin: https://serverchiamante.com"
Se la configurazione è corrette, dovresti ricevere una risposta contenente gli readers impostati. Qualcosa di simile a:
Access-Control-Allow-Origin: example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Verificare la configurazione CORS tramite console del browser
Un altro metofo per testare i cors è quello di aprire un browser e il relativo tab “Strumenti per gli sviluppatori”. A questo punto si può andare nella console e in basso alla pagina digitare:
fetch('https://iegri.com')
Chiaramente al posto di iegri.com dovrete digitare l’FQDN che state proteggendo.
CORS Headers – Quali header si possono configurare
- Access-Control-Allow-Origin: specifica l’origine o le origini autorizzate a fare richieste cross-origin. Può essere impostato su “*” per consentire tutte le origini o su un URL specifico per consentire solo quell’origine. Come detto in precedenza però, l'”*” non sempre funziona tutti i browser. E’ quindi consigliabile specificare nel dettaglio le risorse autorizzate, come specificato in precedenza.
- Access-Control-Allow-Methods: specifica i metodi HTTP consentiti per le richieste cross-origin. Ad esempio, “GET, POST, PUT, DELETE, OPTIONS”. Se il vostro sito non ha form, per esempio, potete lasciare attivo solo il metodo GET. Agite nell’ottica “less is more“. Togliete ciò che non serve.
- Access-Control-Allow-Headers: specifica i headers HTTP consentiti per le richieste cross-origin. Ad esempio, “Content-Type, Authorization”. Nella configurazione CORS definitiva c’è una lunga lista di HTTP headers configurabili.
- Access-Control-Max-Age: specifica il periodo di tempo (in secondi) per cui la risposta CORS è valida.
- Access-Control-Allow-Credentials: specifica se le richieste cross-origin possono includere credenziali come cookie e le credenziali di base di autenticazione. Il valore deve essere impostato su “true” per consentire le credenziali.
- Access-Control-Expose-Headers: specifica quali headers sono esposti al browser nella risposta.
CORS Errors – Errori comuni
Spesso ci si imbatte nei CORS solo quando la pagina che state sviluppando non funziona e vi trovate il classico errore nella console del browser.
Vi riporto di seguito la lista degli errori più comuni.
questo errore indica che l’origin della richiesta non è stato specificato nell’header “Access-Control-Allow-Origin” del server.
i metodi consentiti per la richiesta non sono stati specificati nell’header “Access-Control-Allow-Methods” del server.
questo errore indica che gli headers consentiti per la richiesta non sono stati specificati nell’header “Access-Control-Allow-Headers” del server.
l’origine della richiesta non è stata consentita dal server.
questo errore indica che il browser ha bloccato la richiesta a causa della mancanza dell’header “Access-Control-Allow-Origin” del server.
la richiesta include credenziali, ma l’header “Access-Control-Allow-Credentials” del server non è impostato su “true”.
Contributo Video
Come al solito, vi lascio un utile video tutorial che spiega chiaramente corsa sono e come funzionano i CORS.
Conclusione
Come avrete notato, gli argomenti sono tanti e anche abbastanza complessi. Come spesso accade nell’IT, fare pratica aiuta a migliorare e sopratutto a ricordare. Fate i vostri errori. Aiuteranno a ricordare.
Se avete dubbi, sentitevi liberi di commentare. Sarò felice di aiutarvi.
Buon lavoro
Se volete restare aggiornati, potete iscrivervi alla mia newsletter su LinkedIn.
Principal Web Solutions Architect – Technology & Architecture Manager @ Accenture – Google Cloud Certified – Professional Cloud Architect
Parla di #web, #cloud, #websecurity, #webperformance e #webarchitecture