Cum se activează CORS cu HTTPOnly Cookie pentru a securiza simbolul?
Publicat: 2021-12-29În acest articol, vedem cum să activăm CORS (Cross-Origin Resource Sharing) cu cookie-ul HTTPOnly pentru a ne securiza token-urile de acces.
În prezent, serverele backend și clienții frontend sunt implementați pe diferite domenii. Prin urmare, serverul trebuie să activeze CORS pentru a permite clienților să comunice cu serverul în browsere.
De asemenea, serverele implementează autentificarea fără stat pentru o scalabilitate mai bună. Tokenurile sunt stocate și menținute pe partea clientului, dar nu pe partea serverului, precum sesiune. Pentru securitate, este mai bine să stocați token-uri în cookie-uri HTTPOnly.
De ce sunt blocate solicitările Cross-Origin?
Să presupunem că aplicația noastră frontală a fost implementată la https://app.geekflare.com . Un script încărcat în https://app.geekflare.com poate solicita doar resurse de aceeași origine.
Ori de câte ori încercăm să trimitem o cerere cu origini încrucișate către un alt domeniu https://api.geekflare.com sau alt port https://app.geekflare.com:3000 sau altă schemă http://app.geekflare.com , cererea de origine încrucișată va fi blocată de browser.
Dar de ce aceeași cerere blocată de browser să fie trimisă de pe orice server backend folosind cerere curl sau trimisă folosind instrumente precum poștașul fără nicio problemă CORS. Este de fapt pentru securitate pentru a proteja utilizatorii de atacuri precum CSRF (Cross-Site Request Forgery).
Să luăm un exemplu, să presupunem că un utilizator s-a autentificat în propriul cont PayPal în browser. Dacă putem trimite o solicitare cu origini încrucișate către paypal.com dintr-un script încărcat pe alt domeniu malicious.com fără nicio eroare/blocare CORS, așa cum trimitem cererea de aceeași origine.
Atacatorii își pot trimite cu ușurință pagina rău intenționată https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account transformând-o în short-URL pentru a ascunde adresa URL reală. Când utilizatorul face clic pe un link rău intenționat, scriptul încărcat în domeniul malicious.com va trimite o solicitare de origine încrucișată către PayPal pentru a transfera suma utilizatorului în contul PayPal al atacatorului care va fi executat. Toți utilizatorii care s-au conectat la contul lor PayPal și au făcut clic pe acest link rău intenționat își vor pierde banii. Oricine poate fura cu ușurință bani fără cunoștințele utilizatorului unui cont PayPal.
Din motivul de mai sus, browserele blochează toate solicitările de origine încrucișată.
Ce este CORS (Cross-Origin Resource Sharing)?
CORS este un mecanism de securitate bazat pe antet, folosit de server pentru a spune browserului să trimită o solicitare de origine încrucișată de la domenii de încredere.
Serverul activat cu antete CORS utilizate pentru a evita solicitările de origine încrucișată blocate de browsere.
Cum funcționează CORS?
Deoarece serverul și-a definit deja domeniul de încredere în configurația sa CORS. Când trimitem o solicitare către server, răspunsul va spune browserului că domeniul solicitat este de încredere sau nu în antetul său.
Există două tipuri de solicitări CORS:
- Cerere simplă
- Solicitare preflight
Solicitare simplă:

- Browserul trimite cererea către un domeniu de origine încrucișată cu origine (https://app.geekflare.com).
- Serverul trimite înapoi răspunsul corespunzător cu metodele permise și originea permisă.
- După primirea solicitării, browserul va verifica valoarea antetului de origine trimisă ( https://app.geekflare.com ) și valoarea primită pentru controlul accesului-allow-origin ( https://app.geekflare.com ) sunt aceleași sau wildcard (*). În caz contrar, va genera o eroare CORS.
Solicitare preflight:

- În funcție de parametrul de solicitare personalizată din cererea de origine încrucișată, cum ar fi metode (PUT, DELETE) sau anteturi personalizate sau diferite tipuri de conținut, etc. Browserul va decide să trimită o solicitare OPȚIUNI de verificare pentru a verifica dacă cererea reală este sigură de trimis sau nu.
- După primirea răspunsului (codul de stare: 204, ceea ce înseamnă că nu există conținut), browserul va verifica parametrii de acces-control-permitere pentru cererea reală. Dacă parametrii de solicitare sunt permisi de server. Solicitarea reală cu origini încrucișate trimisă și primită
Dacă access-control-allow-origin: * , atunci răspunsul este permis pentru toate originile. Dar nu este sigur decât dacă aveți nevoie de el.
Cum se activează CORS?
Pentru a activa CORS pentru orice domeniu, activați anteturile CORS pentru a permite originea, metodele, anteturile personalizate, acreditările etc.
Browserul citește antetul CORS de pe server și permite cererile reale de la client numai după verificarea parametrilor cererii.
- Access-Control-Allow-Origin: Pentru a specifica domeniile exacte (https://app.geekflate.com, https://lab.geekflare.com) sau wildcard(*)
- Acces-Control-Permite-Metode: Pentru a permite metodele HTTP (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) de care avem nevoie doar.
- Access-Control-Allow-Headers: pentru a permite numai anumite antete (autorizare, csrf-token)
- Access-Control-Allow-Credentials: valoare booleană utilizată pentru a permite acreditările de origine încrucișată (cookie-uri, antet de autorizare).
- Access-Control-Max-Age: Spune browserului să memoreze în cache răspunsul preflight pentru ceva timp.
- Access-Control-Expose-Headers: specificați anteturile care sunt accesibile prin script-ul clientului.
Pentru a activa CORS în apache și serverul web Nginx, urmați acest tutorial.
Activarea CORS în ExpressJS
Să luăm un exemplu de aplicație ExpressJS fără 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') })În exemplul de mai sus, am activat terminalul API al utilizatorilor pentru metodele POST, PUT, GET, dar nu și metoda DELETE.
Pentru activarea simplă a CORS în aplicația ExpressJS, puteți instala cors
npm install corsAcces-Control-Permite-Origine
Activarea CORS pentru toate domeniile
app.use(cors({ origin: '*' }));Activarea CORS pentru un singur domeniu
app.use(cors({ origin: 'https://app.geekflare.com' }));Dacă doriți să permiteți CORS pentru origine https://app.geekflare.com și https://lab.geekflare.com
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ] }));Acces-Control-Permite-Metode
Pentru a activa CORS pentru toate metodele, omiteți această opțiune în modulul CORS din ExpressJS. Dar pentru activarea unor metode specifice (GET, POST, PUT).
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'] }));Acces-Control-Permite-anteturi
Folosit pentru a permite ca alte antete decât cele implicite să fie trimise cu cereri reale.

app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'] }));Acces-Control-Permite-Acreditări
Omiteți acest lucru dacă nu doriți să spuneți browserului să permită acreditările la cerere, chiar dacă withCredentials este setat la 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 }));Acces-Control-Max-Age
Pentru a intima browserul să memoreze în cache informațiile de răspuns preflight în cache pentru o secundă specificată. Omiteți acest lucru dacă nu doriți să stocați în cache răspunsul.
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 }));Răspunsul preflight memorat în cache va fi disponibil timp de 10 minute în browser.
Acces-Control-Expunere-anteturi
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'] }));Dacă punem wildcard (*) în exposedHeaders, nu va expune antetul Authorization. Deci trebuie să expunem în mod explicit ca mai jos
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', ] }));Cele de mai sus vor expune toate anteturile și antetul de autorizare.
Ce este un cookie HTTP?
Un cookie este o mică bucată de date pe care serverul o va trimite browserului clientului. La solicitările ulterioare, browserul va trimite toate cookie-urile aferente aceluiași domeniu la fiecare solicitare.
Cookie-ul are atributul său, care poate fi definit pentru a face un cookie să funcționeze diferit după cum avem nevoie.
- Nume Numele cookie-ului.
- valoare: datele cookie-ului respectiv numele cookie-ului
- Domeniu: cookie-urile vor fi trimise numai către domeniul definit
- Cale: cookie-uri trimise numai după calea prefixului URL definită. Să presupunem că am definit calea cookie-urilor ca calea='admin/'. Cookie-urile nu sunt trimise pentru adresa URL https://geekflare.com/expire/, dar sunt trimise cu prefixul URL https://geekflare.com/admin/
- Max-Age/Expires (număr în secundă): când ar trebui să expire cookie-ul. O durată de viață a cookie-ului face cookie-ul invalid după timpul specificat.
- HTTPOnly(Boolean): serverul de backend poate accesa acel cookie HTTPOnly, dar nu și script-ul clientului atunci când este adevărat.
- Securizat (boolean): cookie-urile trimise pe un domeniu SSL/TLS numai atunci când sunt adevărate.
- sameSite(string [Strict, Lax, None]): Folosit pentru a activa/restricționa cookie-urile trimise la solicitările pe mai multe site-uri. Pentru a afla mai multe detalii despre cookie-uri
sameSiteconsultați MDN. Acceptă trei opțiuni Strict, Lax, None. Valoarea securizată a cookie-urilor setată la true pentru configurația cookie sameSite=Niciuna.
De ce cookie HTTPOnly pentru token-uri?
Stocarea jetonului de acces trimis de la server în stocarea pe partea client, cum ar fi stocarea locală , DB indexată și cookie-ul (HTTPOnly nu este setat la adevărat) este mai vulnerabilă la atacul XSS. Să presupunem că oricare dintre paginile tale este slabă la un atac XSS. Atacatorii pot folosi greșit jetoanele de utilizator stocate în browser.
Cookie-urile HTTPOnly sunt setate/obținute doar de server/backend, dar nu și de partea clientului.
Scriptul pe partea clientului este restricționat pentru a accesa acel cookie numai HTTP. Prin urmare, cookie-urile HTTPOnly nu sunt vulnerabile la atacurile XSS și sunt mai sigure. Pentru că este accesibil doar de către server.
Activați cookie-ul HTTPOnly în backend-ul compatibil CORS
Activarea cookie-urilor în CORS necesită configurația de mai jos în aplicație/server.
- Setați antetul Access-Control-Allow-Credentials la adevărat.
- Acces-Control-Allow-Origin și Access-Control-Allow-Headers nu ar trebui să fie un wildcard (*).
- Atributul cookie sameSite ar trebui să fie Nici unul.
- Pentru a activa valoarea sameSite la niciunul, setați valoarea sigură la true: Activați backend-ul cu certificat SSL/TLS pentru a funcționa în numele domeniului.
Să vedem un exemplu de cod care setează un token de acces în modul cookie HTTPOnly după verificarea acreditărilor de conectare.
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') });Puteți configura modulele cookie CORS și HTTPOnly prin implementarea celor patru pași de mai sus în limba și serverul dvs. web.
Puteți urma acest tutorial pentru apache și Nginx pentru activarea CORS urmând pașii de mai sus.
cuCredentials pentru cerere Cross-Origin
Acreditări (cookie, autorizare) trimise implicit cu aceeași cerere de origine. Pentru origini încrucișate, trebuie să specificăm withCredentials la adevărat.
API XMLHttpRequest:
var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://api.geekflare.com/user', true); xhr.withCredentials = true; xhr.send(null);Preluare API:
fetch('http://api.geekflare.com/user', { credentials: 'include' });JQuery Ajax:
$.ajax({ url: 'http://api.geekflare.com/user', xhrFields: { withCredentials: true } });Axios:
axios.defaults.withCredentials = trueConcluzie
Sper că articolul de mai sus vă ajută să înțelegeți cum funcționează CORS și să activați CORS pentru cererile de origine încrucișată pe server. De ce stocarea cookie-urilor în HTTPOnly este securizată și cum se utilizează Credentials în clienți pentru cererile de origine încrucișată.
