Come abilitare CORS con HTTPOnly Cookie per proteggere il token?
Pubblicato: 2021-12-29In questo articolo, vediamo come abilitare CORS (Cross-Origin Resource Sharing) con il cookie HTTPOnly per proteggere i nostri token di accesso.
Al giorno d'oggi, i server di backend e i client di frontend sono distribuiti su domini diversi. Pertanto, il server deve abilitare CORS per consentire ai client di comunicare con il server sui browser.
Inoltre, i server stanno implementando l'autenticazione senza stato per una migliore scalabilità. I token vengono archiviati e gestiti sul lato client, ma non sul lato server come la sessione. Per motivi di sicurezza, è meglio memorizzare i token nei cookie HTTPOnly.
Perché le richieste Cross-Origin sono bloccate?
Supponiamo che la nostra applicazione frontend sia stata distribuita su https://app.geekflare.com . Uno script caricato in https://app.geekflare.com può richiedere solo risorse della stessa origine.
Ogni volta che proviamo a inviare una richiesta cross-origin a un altro dominio https://api.geekflare.com o un'altra porta https://app.geekflare.com:3000 o un altro schema http://app.geekflare.com , il la richiesta cross-origin verrà bloccata dal browser.
Ma perché la stessa richiesta bloccata dal browser può essere inviata da qualsiasi server di backend utilizzando la richiesta curl o inviata utilizzando strumenti come il postino senza alcun problema di CORS. In realtà è per la sicurezza proteggere gli utenti da attacchi come CSRF (Cross-Site Request Forgery).
Facciamo un esempio, supponiamo che un utente abbia effettuato l'accesso al proprio conto PayPal nel browser. Se riusciamo a inviare una richiesta paypal.com a paypal.com da uno script caricato su un altro dominio malicious.com senza alcun errore/blocco CORS, come se inviamo la richiesta della stessa origine.
Gli aggressori possono facilmente inviare la loro pagina dannosa https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account convertendola in un URL breve per nascondere l'URL effettivo. Quando l'utente fa clic su un collegamento dannoso, lo script caricato nel dominio malicious.com invierà una richiesta di origine incrociata a PayPal per trasferire l'importo dell'utente all'account PayPal dell'attaccante che verrà eseguito. Tutti gli utenti che hanno effettuato l'accesso al proprio conto PayPal e hanno fatto clic su questo collegamento dannoso perderanno i loro soldi. Chiunque può facilmente rubare denaro senza che l'utente di un account PayPal ne sia a conoscenza.
Per il motivo di cui sopra, i browser bloccano tutte le richieste cross-origin.
Che cos'è CORS (condivisione di risorse tra le origini)?
CORS è un meccanismo di sicurezza basato sull'intestazione utilizzato dal server per indicare al browser di inviare una richiesta di origine incrociata da domini attendibili.
Il server abilitato con le intestazioni CORS utilizzato per evitare le richieste tra le origini bloccate dai browser.
Come funziona CORS?
Poiché il server ha già definito il suo dominio attendibile nella sua configurazione CORS. Quando inviamo una richiesta al server, la risposta dirà al browser che il dominio richiesto è attendibile o meno nella sua intestazione.
Esistono due tipi di richieste CORS:
- Richiesta semplice
- Richiesta di verifica preliminare
Richiesta semplice:

- Il browser invia la richiesta a un dominio cross-origin con origin (https://app.geekflare.com).
- Il server restituisce la risposta corrispondente con i metodi consentiti e l' origine consentita.
- Dopo aver ricevuto la richiesta, il browser controllerà che il valore dell'intestazione di origine inviata ( https://app.geekflare.com ) e il valore di access-control-allow-origin ricevuto ( https://app.geekflare.com ) siano uguali o carta jolly(*). Altrimenti, genererà un errore CORS.
Richiesta di verifica preliminare:

- A seconda del parametro di richiesta personalizzato dalla richiesta multiorigine come metodi (PUT, DELETE) o intestazioni personalizzate o diverso tipo di contenuto, ecc. Il browser deciderà di inviare una richiesta OPTIONS di verifica preliminare per verificare se la richiesta effettiva è sicura da inviare o no.
- Dopo aver ricevuto la risposta (codice di stato: 204, che significa nessun contenuto), il browser verificherà i parametri di autorizzazione del controllo di accesso per la richiesta effettiva. Se i parametri della richiesta sono consentiti dal server. L'effettiva richiesta cross-origine inviata e ricevuta
Se access-control-allow-origin: * , allora la risposta è consentita per tutte le origini. Ma non è sicuro a meno che tu non ne abbia bisogno.
Come abilitare CORS?
Per abilitare CORS per qualsiasi dominio, abilitare le intestazioni CORS per consentire origine, metodi, intestazioni personalizzate, credenziali, ecc.
Il browser legge l'intestazione CORS dal server e consente le richieste effettive dal client solo dopo aver verificato i parametri di richiesta.
- Access-Control-Allow-Origin: per specificare i domini esatti (https://app.geekflate.com, https://lab.geekflare.com) o il carattere jolly(*)
- Access-Control-Allow-Methods: per consentire i metodi HTTP (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) di cui abbiamo bisogno solo noi.
- Access-Control-Allow-Headers: per consentire solo intestazioni specifiche (autorizzazione, token csrf)
- Access-Control-Allow-Credentials: valore booleano utilizzato per consentire le credenziali incrociate (cookie, intestazione di autorizzazione).
- Access-Control-Max-Age: indica al browser di memorizzare nella cache la risposta preflight per un po' di tempo.
- Access-Control-Expose-Headers: specifica le intestazioni che sono accessibili dallo script lato client.
Per abilitare CORS in Apache e Nginx webserver, segui questo tutorial.
Abilitazione di CORS in ExpressJS
Facciamo un esempio di app ExpressJS senza CORS:
const express = require('express'); const app = express() app.get('/users', function (req, res, next) { res.json({msg: 'user get'}) }); app.post('/users', function (req, res, next) { res.json({msg: 'user create'}) }); app.put('/users', function (req, res, next) { res.json({msg: 'User update'}) }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') })Nell'esempio sopra, abbiamo abilitato l'endpoint API degli utenti per i metodi POST, PUT, GET ma non il metodo DELETE.
Per abilitare facilmente CORS nell'app ExpressJS, puoi installare cors
npm install corsAccesso-Controllo-Consenti-Origine
Abilitazione di CORS per tutti i domini
app.use(cors({ origin: '*' }));Abilitazione di CORS per un singolo dominio
app.use(cors({ origin: 'https://app.geekflare.com' }));Se vuoi consentire CORS per l'origine https://app.geekflare.com e https://lab.geekflare.com
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ] }));Metodi-Consenti-Controllo-Accesso
Per abilitare CORS per tutti i metodi, ometti questa opzione nel modulo CORS in ExpressJS. Ma per abilitare metodi specifici (GET, POST, PUT).

app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'] }));Controllo-Accesso-Consenti-Intestazioni
Utilizzato per consentire l'invio di intestazioni diverse da quelle predefinite con richieste effettive.
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'] }));Accesso-Controllo-Consenti-Credenziali
Ometti questo se non vuoi dire al browser di consentire le credenziali su richiesta anche su withCredentials è impostato su true .
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true }));Controllo-Accesso-Max-Età
Per intimare al browser di memorizzare nella cache le informazioni sulla risposta di verifica preliminare nella cache per un secondo specificato. Omettilo se non vuoi memorizzare nella cache la risposta.
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600 }));La risposta di preflight memorizzata nella cache sarà disponibile per 10 minuti nel browser.
Controllo-Accesso-Esposizione-Intestazioni
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['Content-Range', 'X-Content-Range'] }));Se inseriamo il carattere jolly (*) inposedHeaders, non esporrà l'intestazione di autorizzazione. Quindi dobbiamo esporre esplicitamente come di seguito
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization', ] }));Quanto sopra esporrà anche tutte le intestazioni e l'intestazione di autorizzazione.
Che cos'è un cookie HTTP?
Un cookie è un piccolo pezzo di dati che il server invierà al browser client. Alle successive richieste, il browser invierà ad ogni richiesta tutti i cookie relativi allo stesso dominio.
Il cookie ha il suo attributo, che può essere definito per far funzionare un cookie in modo diverso in base alle nostre esigenze.
- Nome Nome del cookie.
- valore: dati del cookie relativi al nome-cookie
- Dominio: i cookie verranno inviati solo al dominio definito
- Percorso: cookie inviati solo dopo il percorso del prefisso URL definito. Supponiamo di aver definito il percorso del nostro cookie come path='admin/'. Cookie non inviati per l'URL https://geekflare.com/expire/ ma inviati con prefisso URL https://geekflare.com/admin/
- Max-Age/Expires (numero in secondi): quando dovrebbe scadere il cookie. Una durata del cookie rende il cookie non valido dopo il tempo specificato.
- HTTPOnly(Boolean): il server di backend può accedere a quel cookie HTTPOnly ma non allo script lato client se true.
- Sicuro (booleano): i cookie inviati solo su un dominio SSL/TLS quando sono veri.
- sameSite(string [Strict, Lax, None]): utilizzato per abilitare/limitare i cookie inviati su richieste cross-site. Per conoscere maggiori dettagli sui cookie
sameSitevedere MDN. Accetta tre opzioni Strict, Lax, None. Il valore di protezione del cookie impostato su true per la configurazione del cookie sameSite=Nessuno.
Perché HTTPOnly cookie per i token?
L'archiviazione del token di accesso inviato dal server nell'archiviazione lato client come l'archiviazione locale , il DB indicizzato e il cookie (HTTPOnly non è impostato su true) è più vulnerabile agli attacchi XSS. Supponiamo che una delle tue pagine sia debole per un attacco XSS. Gli aggressori possono abusare dei token utente memorizzati nel browser.
I cookie HTTPOnly vengono impostati/ottenuti solo dal server/backend ma non dal lato client.
Script lato client limitato per accedere a quel cookie HTTPonly. Quindi i cookie HTTPOnly non sono vulnerabili agli attacchi XSS e sono più sicuri. Perché è accessibile solo dal server.
Abilita il cookie HTTPOnly nel backend abilitato per CORS
L'abilitazione dei cookie in CORS richiede la configurazione seguente nell'applicazione/server.
- Imposta l'intestazione Access-Control-Allow-Credentials su true.
- Access-Control-Allow-Origin e Access-Control-Allow-Headers non devono essere caratteri jolly (*).
- L'attributo cookie sameSite deve essere Nessuno.
- Per abilitare il valore sameSite su none, impostare il valore secure su true: Abilita backend con certificato SSL/TLS per funzionare nel nome di dominio.
Vediamo un codice di esempio che imposta un token di accesso nel cookie HTTPOnly dopo aver verificato le credenziali di accesso.
const express = require('express'); const app = express(); const cors = require('cors'); app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization' ] })); app.post('/login', function (req, res, next) { res.cookie('access_token', access_token, { expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //second min hour days year secure: true, // set to true if your using https or samesite is none httpOnly: true, // backend only sameSite: 'none' // set to none for cross-request }); res.json({ msg: 'Login Successfully', access_token }); }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') });Puoi configurare i cookie CORS e HTTPOnly implementando i quattro passaggi precedenti nella tua lingua di backend e nel tuo server web.
Puoi seguire questo tutorial per Apache e Nginx per abilitare CORS seguendo i passaggi precedenti.
con Credenziali per richiesta Cross-Origin
Credenziali (cookie, autorizzazione) inviate con la richiesta della stessa origine per impostazione predefinita. Per l'origine incrociata, dobbiamo specificare withCredentials su true.
API XMLHttpRequest:
var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://api.geekflare.com/user', true); xhr.withCredentials = true; xhr.send(null);Recupera API:
fetch('http://api.geekflare.com/user', { credentials: 'include' });JQuery Ajax:
$.ajax({ url: 'http://api.geekflare.com/user', xhrFields: { withCredentials: true } });Assi:
axios.defaults.withCredentials = trueConclusione
Spero che l'articolo sopra ti aiuti a capire come funziona CORS e ad abilitare CORS per le richieste cross-origin nel server. Perché la memorizzazione dei cookie in HTTPOnly è sicura e come vengono utilizzate le credenziali nei client per le richieste tra origini.
