Sveobuhvatan vodič za razumijevanje i implementaciju dijeljenja resursa između različitih izvora (CORS) za sigurnu JavaScript komunikaciju između različitih domena.
Implementacija sigurnosti između različitih izvora: Najbolje prakse za JavaScript komunikaciju
U današnjem međusobno povezanom webu, JavaScript aplikacije često trebaju komunicirati s resursima s različitih izvora (domena, protokola ili portova). Ovu interakciju regulira pravilo istog izvora (Same-Origin Policy) preglednika, ključni sigurnosni mehanizam dizajniran da spriječi zlonamjerne skripte u pristupu osjetljivim podacima preko granica domena. Međutim, legitimna komunikacija između različitih izvora često je nužna. Tu na scenu stupa dijeljenje resursa između različitih izvora (Cross-Origin Resource Sharing - CORS). Ovaj članak pruža sveobuhvatan pregled CORS-a, njegove implementacije i najboljih praksi za sigurnu komunikaciju između različitih izvora u JavaScriptu.
Razumijevanje pravila istog izvora (Same-Origin Policy)
Pravilo istog izvora (Same-Origin Policy - SOP) temeljni je sigurnosni koncept u web preglednicima. Ograničava skripte koje se izvode na jednom izvoru od pristupa resursima s drugog izvora. Izvor je definiran kombinacijom protokola (npr. HTTP ili HTTPS), naziva domene (npr. example.com) i broja porta (npr. 80 ili 443). Dva URL-a imaju isti izvor samo ako se sve tri komponente točno podudaraju.
Na primjer:
http://www.example.comihttp://www.example.com/path: Isti izvorhttp://www.example.comihttps://www.example.com: Različit izvor (različit protokol)http://www.example.comihttp://subdomain.example.com: Različit izvor (različita domena)http://www.example.com:80ihttp://www.example.com:8080: Različit izvor (različit port)
SOP je ključna obrana od napada skriptiranja na više stranica (Cross-Site Scripting - XSS), gdje zlonamjerne skripte ubrizgane u web stranicu mogu ukrasti korisničke podatke ili izvršiti neovlaštene radnje u ime korisnika.
Što je dijeljenje resursa između različitih izvora (CORS)?
CORS je mehanizam koji koristi HTTP zaglavlja kako bi poslužiteljima omogućio da naznače kojim je izvorima (domenama, shemama ili portovima) dopušten pristup njihovim resursima. U suštini, opušta pravilo istog izvora za određene zahtjeve s različitih izvora, omogućujući legitimnu komunikaciju dok i dalje štiti od zlonamjernih napada.
CORS funkcionira dodavanjem novih HTTP zaglavlja koja specificiraju dopuštene izvore i metode (npr. GET, POST, PUT, DELETE) koje su dopuštene za zahtjeve s različitih izvora. Kada preglednik uputi zahtjev s različitog izvora, šalje zaglavlje Origin sa zahtjevom. Poslužitelj odgovara sa zaglavljem Access-Control-Allow-Origin koje specificira dopušteni izvor(e). Ako se izvor zahtjeva podudara s vrijednošću u zaglavlju Access-Control-Allow-Origin (ili ako je vrijednost *), preglednik dopušta JavaScript kodu pristup odgovoru.
Kako CORS funkcionira: Detaljno objašnjenje
CORS proces obično uključuje dvije vrste zahtjeva:
- Jednostavni zahtjevi: To su zahtjevi koji zadovoljavaju određene kriterije. Ako zahtjev zadovoljava te uvjete, preglednik izravno šalje zahtjev.
- Preflight zahtjevi: To su složeniji zahtjevi koji zahtijevaju da preglednik prvo pošalje "preflight" OPTIONS zahtjev poslužitelju kako bi utvrdio je li stvarni zahtjev siguran za slanje.
1. Jednostavni zahtjevi
Zahtjev se smatra "jednostavnim" ako zadovoljava sve sljedeće uvjete:
- Metoda je
GET,HEADiliPOST. - Ako je metoda
POST, zaglavljeContent-Typeje jedno od sljedećih: application/x-www-form-urlencodedmultipart/form-datatext/plain- Nisu postavljena prilagođena zaglavlja.
Primjer jednostavnog zahtjeva:
GET /resource HTTP/1.1
Origin: http://www.example.com
Primjer odgovora poslužitelja koji dopušta izvor:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Content-Type: application/json
{
"data": "Some data"
}
Ako je zaglavlje Access-Control-Allow-Origin prisutno i njegova vrijednost se podudara s izvorom zahtjeva ili je postavljena na *, preglednik dopušta skripti pristup podacima odgovora. U suprotnom, preglednik blokira pristup odgovoru, a u konzoli se prikazuje poruka o pogrešci.
2. Preflight zahtjevi
Zahtjev se smatra "preflighted" ako ne zadovoljava kriterije za jednostavan zahtjev. To se obično događa kada zahtjev koristi drugu HTTP metodu (npr. PUT, DELETE), postavlja prilagođena zaglavlja ili koristi Content-Type različit od dopuštenih vrijednosti.
Prije slanja stvarnog zahtjeva, preglednik prvo šalje OPTIONS zahtjev poslužitelju. Ovaj "preflight" zahtjev uključuje sljedeća zaglavlja:
Origin: Izvor stranice koja postavlja zahtjev.Access-Control-Request-Method: HTTP metoda koja će se koristiti u stvarnom zahtjevu (npr.PUT,DELETE).Access-Control-Request-Headers: Popis prilagođenih zaglavlja odvojenih zarezom koja će biti poslana u stvarnom zahtjevu.
Primjer preflight zahtjeva:
OPTIONS /resource HTTP/1.1
Origin: http://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header, Content-Type
Poslužitelj mora odgovoriti na OPTIONS zahtjev sa sljedećim zaglavljima:
Access-Control-Allow-Origin: Izvor kojem je dopušteno uputiti zahtjev (ili*za dopuštenje bilo kojeg izvora).Access-Control-Allow-Methods: Popis HTTP metoda odvojenih zarezom koje su dopuštene za zahtjeve s različitih izvora (npr.GET,POST,PUT,DELETE).Access-Control-Allow-Headers: Popis prilagođenih zaglavlja odvojenih zarezom koja su dopuštena za slanje u zahtjevu.Access-Control-Max-Age: Broj sekundi tijekom kojih preglednik može predmemorirati (cache) preflight odgovor.
Primjer odgovora poslužitelja na preflight zahtjev:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400
Ako odgovor poslužitelja na preflight zahtjev pokazuje da je stvarni zahtjev dopušten, preglednik će tada poslati stvarni zahtjev. U suprotnom, preglednik će blokirati zahtjev i prikazati poruku o pogrešci.
Implementacija CORS-a na strani poslužitelja
CORS se primarno implementira na strani poslužitelja postavljanjem odgovarajućih HTTP zaglavlja u odgovoru. Specifični detalji implementacije varirat će ovisno o tehnologiji na strani poslužitelja koja se koristi.
Primjer korištenja Node.js s Expressom:
const express = require('express');
const cors = require('cors');
const app = express();
// Omogući CORS za sve izvore
app.use(cors());
// Alternativno, konfigurirajte CORS za određene izvore
// const corsOptions = {
// origin: 'http://www.example.com'
// };
// app.use(cors(corsOptions));
app.get('/resource', (req, res) => {
res.json({ message: 'This is a CORS-enabled resource' });
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
cors middleware pojednostavljuje proces postavljanja CORS zaglavlja u Expressu. Možete omogućiti CORS za sve izvore koristeći cors() ili ga konfigurirati za određene izvore koristeći cors(corsOptions).
Primjer korištenja Pythona s Flaskom:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/resource")
def hello():
return {"message": "This is a CORS-enabled resource"}
if __name__ == '__main__':
app.run(debug=True)
Ekstenzija flask_cors pruža jednostavan način za omogućavanje CORS-a u Flask aplikacijama. Možete omogućiti CORS za sve izvore prosljeđivanjem app u CORS(). Također je moguća konfiguracija za određene izvore.
Primjer korištenja Jave sa Spring Bootom:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/resource")
.allowedOrigins("http://www.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "X-Custom-Header")
.allowCredentials(true)
.maxAge(3600);
}
}
U Spring Bootu, možete konfigurirati CORS koristeći WebMvcConfigurer. Ovo omogućuje finu kontrolu nad dopuštenim izvorima, metodama, zaglavljima i drugim CORS postavkama.
Izravno postavljanje CORS zaglavlja (Opći primjer)
Ako ne koristite nijedan framework, možete postaviti zaglavlja izravno u svom kodu na strani poslužitelja (npr. PHP, Ruby on Rails, itd.):
Najbolje prakse za CORS
Kako biste osigurali sigurnu i učinkovitu komunikaciju između različitih izvora, slijedite ove najbolje prakse:
- Izbjegavajte korištenje
Access-Control-Allow-Origin: *u produkciji: Dopuštanje svim izvorima da pristupe vašim resursima može predstavljati sigurnosni rizik. Umjesto toga, navedite točne izvore koji su dopušteni. - Koristite HTTPS: Uvijek koristite HTTPS i za izvor koji postavlja zahtjev i za poslužiteljski izvor kako biste zaštitili podatke u prijenosu.
- Validirajte ulazne podatke: Uvijek validirajte i sanitizirajte podatke primljene iz zahtjeva s različitih izvora kako biste spriječili napade ubacivanjem koda (injection attacks).
- Implementirajte pravilnu autentifikaciju i autorizaciju: Osigurajte da samo ovlašteni korisnici mogu pristupiti osjetljivim resursima.
- Predmemorirajte preflight odgovore: Koristite
Access-Control-Max-Ageza predmemoriranje preflight odgovora i smanjenje brojaOPTIONSzahtjeva. - Razmislite o korištenju vjerodajnica (credentials): Ako vaš API zahtijeva autentifikaciju s kolačićima ili HTTP autentifikacijom, morate postaviti zaglavlje
Access-Control-Allow-Credentialsnatruena poslužitelju i opcijucredentialsna'include'u vašem JavaScript kodu (npr. kod korištenjafetchiliXMLHttpRequest). Budite izuzetno oprezni pri korištenju ove opcije, jer može uvesti sigurnosne ranjivosti ako se ne rukuje pravilno. Također, kada je Access-Control-Allow-Credentials postavljen na true, Access-Control-Allow-Origin ne može biti postavljen na "*". Morate eksplicitno navesti dopušteni izvor(e). - Redovito pregledavajte i ažurirajte CORS konfiguraciju: Kako se vaša aplikacija razvija, redovito pregledavajte i ažurirajte svoju CORS konfiguraciju kako biste osigurali da ostane sigurna i zadovoljava vaše potrebe.
- Razumijte implikacije različitih CORS konfiguracija: Budite svjesni sigurnosnih implikacija različitih CORS konfiguracija i odaberite onu koja je prikladna za vašu aplikaciju.
- Testirajte svoju CORS implementaciju: Temeljito testirajte svoju CORS implementaciju kako biste osigurali da radi kako je očekivano i da ne uvodi nikakve sigurnosne ranjivosti. Koristite alate za razvojne programere u pregledniku za inspekciju mrežnih zahtjeva i odgovora, te koristite alate za automatsko testiranje za provjeru ponašanja CORS-a.
Primjer: Korištenje Fetch API-ja s CORS-om
Evo primjera kako koristiti fetch API za upućivanje zahtjeva s različitog izvora:
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors', // Govori pregledniku da je ovo CORS zahtjev
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
}
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
Opcija mode: 'cors' govori pregledniku da je ovo CORS zahtjev. Ako poslužitelj ne dopušta izvor, preglednik će blokirati pristup odgovoru, i bit će bačena pogreška.
Ako koristite vjerodajnice (npr. kolačiće), morate postaviti opciju credentials na 'include':
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors',
credentials: 'include', // Uključi kolačiće u zahtjev
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
// ...
});
CORS i JSONP
JSON s podstavom (JSONP) je starija tehnika za zaobilaženje pravila istog izvora. Funkcionira dinamičkim stvaranjem <script> taga koji učitava podatke s druge domene. Iako JSONP može biti koristan u određenim situacijama, ima značajna sigurnosna ograničenja i treba ga izbjegavati kad god je to moguće. CORS je preferirano rješenje za komunikaciju između različitih izvora jer pruža sigurniji i fleksibilniji mehanizam.
Ključne razlike između CORS-a i JSONP-a:
- Sigurnost: CORS je sigurniji od JSONP-a jer dopušta poslužitelju da kontrolira kojim je izvorima dopušten pristup njegovim resursima. JSONP ne pruža nikakvu kontrolu izvora.
- HTTP Metode: CORS podržava sve HTTP metode (npr.
GET,POST,PUT,DELETE), dok JSONP podržava samoGETzahtjeve. - Rukovanje pogreškama: CORS pruža bolje rukovanje pogreškama od JSONP-a. Kada CORS zahtjev ne uspije, preglednik pruža detaljne poruke o pogreškama. Rukovanje pogreškama u JSONP-u ograničeno je na otkrivanje je li se skripta uspješno učitala.
Rješavanje problema s CORS-om
Problemi s CORS-om mogu biti frustrirajući za otklanjanje. Evo nekoliko uobičajenih savjeta za rješavanje problema:
- Provjerite konzolu preglednika: Konzola preglednika obično će pružiti detaljne poruke o pogreškama vezanim uz CORS.
- Pregledajte mrežne zahtjeve: Koristite alate za razvojne programere u pregledniku za inspekciju HTTP zaglavlja i zahtjeva i odgovora. Provjerite jesu li zaglavlja
OriginiAccess-Control-Allow-Originispravno postavljena. - Provjerite konfiguraciju na strani poslužitelja: Dvaput provjerite svoju CORS konfiguraciju na strani poslužitelja kako biste osigurali da dopušta ispravne izvore, metode i zaglavlja.
- Očistite predmemoriju preglednika: Ponekad predmemorirani preflight odgovori mogu uzrokovati probleme s CORS-om. Pokušajte očistiti predmemoriju preglednika ili koristiti privatni prozor za pregledavanje.
- Koristite CORS proxy: U nekim slučajevima, možda ćete morati koristiti CORS proxy za zaobilaženje CORS ograničenja. Međutim, budite svjesni da korištenje CORS proxyja može uvesti sigurnosne rizike.
- Provjerite pogrešne konfiguracije: Potražite uobičajene pogrešne konfiguracije kao što su nedostajuće zaglavlje
Access-Control-Allow-Origin, netočne vrijednostiAccess-Control-Allow-MethodsiliAccess-Control-Allow-Headers, ili netočno zaglavljeOriginu zahtjevu.
Zaključak
Dijeljenje resursa između različitih izvora (CORS) ključan je mehanizam za omogućavanje sigurne komunikacije između različitih izvora u JavaScript aplikacijama. Razumijevanjem pravila istog izvora, tijeka rada CORS-a i različitih uključenih HTTP zaglavlja, programeri mogu učinkovito implementirati CORS kako bi zaštitili svoje aplikacije od sigurnosnih ranjivosti, istovremeno dopuštajući legitimne zahtjeve s različitih izvora. Slijedeći najbolje prakse za konfiguraciju CORS-a i redovito pregledavanje vaše implementacije ključni su za održavanje sigurne i robusne web aplikacije.
Ovaj sveobuhvatni vodič pruža čvrst temelj za razumijevanje i implementaciju CORS-a. Ne zaboravite konzultirati službenu dokumentaciju i resurse za vašu specifičnu tehnologiju na strani poslužitelja kako biste osigurali da implementirate CORS ispravno i sigurno.