U ovom postu ćemo proći kroz CORS, šta je i za šta se koristi.
Šta je CORS ?
Cross-Origin Resource Sharing (CORS) je HTTP-header-based mehanizam koji omogućava serveru da pokazuje bilo koje porijeklo (domena, schema ili port) kojem će browser dozvoliti da downloaduje resurse, ne ukljućujući samog sebe jer je to omogućeno defaultno. CORS se također oslanja na mehanizam kojim browser šalje "preflight" request serveru na kojem se nalazi cross-origin resurs, da bi provjerio da li će taj server prihvatiti request. U tom preflight requestu browser šalje headers koji sadrže HTTP metodu i headers koji će biti u pravom requestu.
Na primjer, ako želimo da download podatke sa stranice example-b.com, i pokušavamo da ih download sa stranice example-a.com, mi prvo moram da dozvolimo na stranici example-b.com ko može da pošalje requeste, kod nas je to example-a.com i nakon toga example-a.com može da šalje request na stranicu example-b.com. Ovo moramo da uradimo jer browser defaultno ne dozvoljava da se šalju request na stranice koje nisu dali dozvolu za to. Imamo dvije vrste requesta koji šaljemo sa browser: two types of requests on frontend:
- Simple
- Preflight
Simple je tip requesta koji se koristi kada želimo da pošaljemo određene HTTP Metode (GET, HEAD, POST) koji ne zahtjevaju dodatni "preflight" request (OPTIONS) da se pošalje da provjeri da li je request dovoljen prije nego pošalje naš request. Ukoliko dodamo custom headers u nas request, taj request više nije simple tipa, i prije njega browser mora da pošalje "preflight" da bi provjerio sa serverom da li imamo dozvolu za naš request. Ovo je animacija simple requesta:
Animation by Fatima Trtak
Preflight je tip requesta koji koristi dodatni OPTIONS request koji browser pošalje automatski prije nego se pošalje naš request da bi se provjerilo sa serve da li imamo dozvolu i da li taj server podržava HTTP metodu koju pokušavamo izvršiti. Neke od metoda koje koriste ovaj tip requesta su (DELETE, PATCH, PUT, ...). Ispod je slika koja prikazuje preflight request:
Animation by Fatima Trtak
U ovim slikama iznad može se vidjeti par headera koji se koriste sa CORS: Access-Control-Allow-Origin, Access-Control-Request-Method i Access-Control-Request-Headers. Njih ćemo objasniti kasnije u ovom postu.
NOTE: CORS se koristi samo za requeste koji se upućuju na servere na drugim domenama, jer kada su na istoj domeni/serveru mi imamo kontrolu nad resursima i onda su manje šanse da se desi neki security propust. Tako da po default nemamo omogućen CORS na requestima koji su na istoj domen/serveru. Na primjer, ukoliko imamo frontend aplikaciju na localhost:3000 i backend podignut na localhost:5000, svaki request sa frontenda na backend imat ćemo CORS omogućen na tim requests jer su oba tretirana kao odvojene domene jer nisu na istim portovima. Da bi omogućili da se ti requesti šalju bez CORS mogžemo koristiti nešto kao što je NGINX koji će biti na portu npr. 80, i filtrirat će sve zahtjeve koje dobijemo, znaći kada pošaljemo zahtjev na localhost:80 NGINX će preusmjeriti frontend pozive na localhost:3000 a backend na localhost:5000 i tako nećemo dobiti CORS od browsera jer je sve na istoj domeni. Ukoliko želite proćitati više o NGINX i kako se namjesti posjetite ovaj link
Kako riješiti CORS issue
Svi smo se mi morali susreti sa CORS tokom developmenta i vjerovatno smo pronašli rješenje na internetu i samo ga dodali bez da znamo šta ustvari to rješenje radi. Gledali smo samo da riješi CORS problem i nastavili dalje development. Ali generalno bi trebali praktikovati da razumijemo šta dodajemo u projekat da ne bi slučajno dodali neočekivane greške. CORS je veliki dio web developmenta i trebali bi ga općenito bolje razumjeti. Ne zahtjeva previše da ga naučimo ali ga ipak riješimo dodavanjem gotovih rješenja sa interneta. Kao što nas je većina uradila, našli slično rješenje ovome ispod i dodali ga u svoj code:
1res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");2res.setHeader(3 "Access-Control-Allow-Methods",4 "GET, POST, OPTIONS, PUT, PATCH, DELETE"5);6res.setHeader("Access-Control-Allow-Headers", "X-Something,content-type");7res.setHeader("Access-Control-Allow-Credentials", true);
Ovo je standradno rješenje za CORS isse, gdje ga samo kopiramo u naš code, čak i ako naš code ima samo POST i GET metode, mi dodamo za PUT, PATCH i DELETE metode access bez ikakvog razloga za to. Access-Control-Allow-Credentials: true, znaći da želimo da dodamo kredencijale sa našim requestima. Kredencijali mogu biti cookies ili authorization headers ili TLS client certificates. Ako dodamo credentials u naš request body onda ćemo imate preflight request koji će provjeriti da li server također ima omogućene credentials. Ako ima omogućene, browser će automatski dodati cookie za tu domenu koja pravi request i ako server odgovori sa Access-Control-Allow-Credentials: true onda mi dobijemo response sa našom datom koju smo tražili, a ako ne browser će reject taj request i baciti error. Simple GET requesti nemaju preflight provjeru ali ako dodamo credentials u naš request body onda ćemo imati provjeru koja će dobiti odgovor od servera da li su omogućeni kredencijali, ako jesu dobijemo response ako ne, browser će samo ignorisati response sa servera.
I kao što možete vidjeti mi šaljemo dosta headera sa servera iako ih ne trebamo, ukoliko ne trebaju credentials u requestima onda ne trebamo dodavati Access-Control-Allow-Credentials u code, i ako nemam neku od metoda na serveru onda ih trebamo ukloniti iz Access-Control-Allow-Methods, tako da naš code sadrži samo ono što nam je potrebno.
Sada kada smo vidjeli primjer gdje se rješenje kopira sa interneta, pogledajmo kako mi možemo da riješimo CORS bez da pretražujemo internet i da razumijemo šta se dešava u code.
Prvi header koji bi trebali dodati je Access-Control-Allow-Origin, onda Access-Control-Allow-Headers, Access-Control-Allow-Methods, i Access-Control-Allow-Credentials ovisno o tome da li imamo authorization na serveru ili nemamo.
Access-Control-Allow-Origin
Ovaj header kontroliše na serveru ko ustvari može da traži i dobije podatke, i ako nemamo na serveru ovaj header niko neće moći dobiti podatke i izbacivat će im CORS error. Prvu vrijednost koju možemo da stavimo u ovaj i inače se koristi u večini slučajeva jeste wildcard *. Kada dodamo Access-Control-Allow-Origin: *, znaći da bilo ko može da traži datu sa našeg servera i da je dobije. Inače datu koju dodajemo na server je dostupna svima tako da ovaj header to omogućava. Slijedeća opcija je da dodamo u ovaj header listu domena koje mogu da traže datu sa našeg servera. Na primjer ako stavimo Access-Control-Allow-Origin: localhost:3000,ovo znaći da jedino localhost:3000 može da pristupi našem serveru i dobije podatke i niko više, ovo je dobra opcija za kompanije koje žele da komuniciraju samo sa određenim domenama zbog dodatne sigurnosti.
Access-Control-Allow-Headers
Ovdje dodajemo listu headera koje naš server podržava i očekuje, npr. ako imamo authentikaciju "x-key" koji očekujemo na našem backend, onda ga dodamo u ovaj header tako da naš server može reći browseru da podržavamo ovaj header i da može da pošalje request koji ga sadrži. Ako imamo default headere, onda možemo da ne dodajemo ovaj header i browser će sam da handle ovaj header.
Access-Control-Allow-Methods
Ovdje dodajemo metode koje naš server podržava, tako da ukoliko pošalju metodu koju ne podržavamo izbacit će im CORS error. U ovaj header bi trebali dodati samo metode koje podržavamo tako da nam je lakše pratiti šta se dešava. Nemojte dodavati sve u slučaju da vam nekad neka zatreba, dodajte samo one koje podržava server.
Access-Control-Allow-Credentials
Ovaj header smo već spomenuli prije u ovom postu, ali ukratko ovaj header koristimo kada želimo da šaljemo kredencijale serveru. Cookies, authorization headers, ili TLS client certificates. Ovo trebamo samo kada šaljemo kredencijale, u svim ostalim ne trebamo ga dodati jer je po default vrijednot false za ovaj header.
Ovi headeri su najčešće korišteni da se enable CORS i da rade requests kao što je i planirano. U slučaju da vi imate drugačije zahtjeve na vašem projektu možete istražiti više o ovome, najbolje mjesto je MDN gdje možete naći skoro sve informacije koje vam trebaju.
Zaključak
Sada bi trebali imati bolje razumijevanje CORS i kako da ga riješimo, sada nam ne treba kopirano rješenje sa interneta jer možemo sami da ga uradimo. Ukoliko vam je ova tema interesantna i ukoliko vam je pomogla, podijelite je i sa drugim pa da i onu mogu naućiti o ovoj temi.