CORS začína pri same-origin policy
Same-origin policy je základný bezpečnostný mechanizmus prehliadača. Obmedzuje možnosť dokumentu alebo skriptu čítať dáta z iného originu a bráni napríklad škodlivej stránke čítať dáta zo služby, kde je používateľ prihlásený. [3]
CORS, Cross-Origin Resource Sharing, je HTTP mechanizmus, ktorým server túto zásadu kontrolovane uvoľňuje a určuje, ktoré originy môžu odpoveď čítať. Fetch Standard ho opisuje ako opt-in protokol. [1][2]
Čo presne znamená origin
| Stránka | Cieľová URL | Vzťah | Prečo |
|---|---|---|---|
| https://app.example.com | https://app.example.com/api | Rovnaký origin | Rovnaká schéma, host a port |
| https://app.example.com | https://api.example.com | Iný origin | Iný host |
| https://app.example.com | http://app.example.com | Iný origin | Iná schéma |
| https://app.example.com | https://app.example.com:8443 | Iný origin | Iný port |
| https://app.example.com | https://app.example.com/v2 | Rovnaký origin | Mení sa iba cesta |
Origin je daný schémou, hostom a portom. Dve URL sú same-origin iba vtedy, keď sa zhodujú všetky tri hodnoty. Cesta nehrá úlohu. [3][4]
`https://app.example.com` a `https://api.example.com` sú teda rôzne originy. Rovnako HTTP a HTTPS alebo rozdielne porty. [4]
Same-origin a same-site nie sú totožné pojmy. Je to dôležité pri cookies, pretože `SameSite` používa pojem site, zatiaľ čo CORS používa origin. [13]
Čo naozaj znamená, že prehliadač blokuje API
Tvrdenie, že prehliadač blokuje request, je často príliš zjednodušené. Pri jednoduchom cross-origin requeste môže request odoslať, prijať platnú HTTP odpoveď a až potom ju nesprístupniť JavaScriptu, ak CORS kontrola zlyhá. [1][6]
Pri requestoch s preflightom je priebeh iný. Najprv sa posiela `OPTIONS`. Ak preflight zlyhá, skutočný request sa neodošle. [2][5][6]
Tento rozdiel je dôležitý pri operáciách meniacich dáta. CORS chyba automaticky neznamená, že backend žiadny request nedostal.
Jednoduché requesty: kedy sa preflight nepoužije
| Metóda | Príklad headers | Jednoduchý request | Dôsledok |
|---|---|---|---|
| GET | Accept | Áno | Bez preflightu |
| POST | Content-Type: text/plain | Áno | Bez preflightu |
| POST | Content-Type: application/json | Nie | JSON vyvolá preflight |
| PUT | Content-Type: application/json | Nie | Metóda vyvolá preflight |
| GET | Authorization: Bearer ... | Nie | Header vyvolá preflight |
Request je jednoduchý iba pri splnení CORS safelist. Povolené metódy sú `GET`, `HEAD` a `POST`. Ručne nastavené headers musia byť safelisted a `Content-Type` smie byť iba `application/x-www-form-urlencoded`, `multipart/form-data` alebo `text/plain`. [1]
Bežný `POST` s `Content-Type: application/json` nie je jednoduchý a zvyčajne vyvolá preflight. Rovnako `PUT`, `DELETE` alebo vlastný header typu `X-Request-ID`. [1][11]
Preflight OPTIONS: čo prehliadač overuje
Preflight je automatický `OPTIONS` request. Prehliadač posiela `Origin`, `Access-Control-Request-Method` a podľa potreby `Access-Control-Request-Headers`. Server odpovie povolenými originmi, metódami a headers. [2][5]
Ak chce aplikácia poslať `DELETE` s `Authorization`, prehliadač môže najprv overiť, či daný origin smie použiť túto metódu a header. Až kladná odpoveď dovolí skutočný request. [5]
Preflight pridáva ďalší sieťový round trip, ale výsledok možno uložiť do samostatnej CORS preflight cache mimo bežnej HTTP cache. [5][10]
Najdôležitejšie CORS headers
| Header | Smer | Účel |
|---|---|---|
| Origin | Request | Origin, ktorý request iniciuje |
| Access-Control-Allow-Origin | Response | Origin, ktorého kód môže odpoveď dostať |
| Access-Control-Allow-Methods | Preflight response | Metódy povolené preflightom |
| Access-Control-Allow-Headers | Preflight response | Request headers povolené preflightom |
| Access-Control-Allow-Credentials | Response | Povolenie sprístupniť odpoveď s credentials |
| Access-Control-Expose-Headers | Response | Ďalšie response headers dostupné JavaScriptu |
| Access-Control-Max-Age | Preflight response | Doba cache výsledku preflightu |
| Vary: Origin | Response | Informácia pre cache, že odpoveď závisí od Origin |
`Origin` je request header označujúci iniciujúci origin. `Access-Control-Allow-Origin` je serverový response header, ktorý hovorí prehliadaču, či môže odpoveď zdieľať s kódom z daného originu. [1][2][8]
`Access-Control-Allow-Methods` a `Access-Control-Allow-Headers` sú dôležité najmä pri preflighte. `Access-Control-Allow-Credentials` povoľuje sprístupnenie odpovede s credentials a `Access-Control-Expose-Headers` sprístupní JavaScriptu ďalšie response headers. [1][2]
Ak server nastavuje `Access-Control-Allow-Origin` dynamicky, MDN odporúča aj `Vary: Origin`. [1]
`Access-Control-Allow-Origin`: wildcard alebo konkrétny origin
`Access-Control-Allow-Origin: *` povoľuje zdieľanie odpovede s ľubovoľným originom pri requestoch bez credentials. Pre skutočne verejné API to môže byť správne. [2][8]
Pri privátnych alebo citlivých endpointoch by mala byť povolená množina čo najmenšia. MDN a OWASP odporúčajú konkrétne originy, ak nie je globálny prístup potrebný. [14][17]
Pri viacerých povolených originoch má server porovnať prichádzajúci `Origin` s allowlistom a vrátiť iba schválenú hodnotu. Slepé zrkadlenie ľubovoľného originu je nebezpečné. [8][16]
Cookies a credentials: častý zdroj problémov
Fetch môže použiť `credentials: "include"` pre credentials v cross-origin requests. To samo nestačí. Server musí vrátiť `Access-Control-Allow-Credentials: true` a konkrétny `Access-Control-Allow-Origin`. `*` tu nie je povolené. [2][6][9]
Cookies podliehajú aj vlastným pravidlám. `SameSite` môže zabrániť ich odoslaniu pri cross-site requeste a `SameSite=None` vyžaduje `Secure`. [13]
CORS a `SameSite` riešia odlišné problémy. CORS môže byť správne, ale cookie chýba, alebo cookie môže odísť a CORS stále zablokuje JavaScriptu odpoveď.
Prečo `mode: "no-cors"` zvyčajne nič neopraví
`fetch(..., { mode: "no-cors" })` neobchádza CORS, ak aplikácia potrebuje odpoveď čítať. Metódy a headers sú obmedzené a odpoveď je opaque. JavaScript nemôže čítať body ani headers a viditeľný status je `0`. [6][7][11]
`no-cors` má špecializované použitie, napríklad niektoré scenáre Service Worker, ale pre bežné JSON API je zvyčajne zlou voľbou. [6]
Ak externé API CORS neponúka, vlastný backend alebo proxy môže vykonať request server-to-server. [11][12]
Prečo curl, Postman alebo backend môžu fungovať
Same-origin policy a CORS vynucuje prehliadač. HTTP klient mimo tohto modelu rovnakou CORS kontrolou neprechádza. Preto môže endpoint fungovať v curl alebo API nástroji a z JavaScriptu stránky byť blokovaný. [2][3][16]
To tiež ukazuje, prečo CORS nie je autentizácia API. Klient mimo prehliadača môže nastaviť vlastný `Origin`, takže OWASP varuje pred použitím tohto headeru ako dôkazu identity. [16]
API stále potrebuje autorizáciu, tokeny, sessions, kontroly oprávnení a serverovú validáciu.
CORS nenahrádza CSRF ochranu ani autorizáciu
Same-origin policy obmedzuje najmä cross-origin čítanie. Formuláre a niektoré jednoduché requesty možno napriek tomu posielať cross-origin, takže cookie-based aplikácie stále potrebujú CSRF ochranu. [3][15]
OWASP výslovne odporúča nespoliehať sa iba na CORS alebo `Origin` pri ochrane citlivých zdrojov. Autentizácia a autorizácia sú potrebné nezávisle. [16]
CORS odpovedá na otázku, či prehliadač môže sprístupniť odpoveď kódu z originu. Nerieši, či používateľ alebo klient smie operáciu vykonať.
Časté chyby a diagnostika
Chýbajúci `Access-Control-Allow-Origin` je bežná chyba. Ďalšie príčiny sú nepovolená metóda, chýbajúca položka v `Access-Control-Allow-Headers`, `*` s credentials, chybná OPTIONS odpoveď alebo redirecty v CORS flow. [11][12]
JavaScript dostáva zámerne iba obmedzené detaily. Pre presnú diagnostiku je dôležitejšia konzola a Network panel. [1][11]
`CORS request did not succeed` môže spôsobiť aj DNS, timeout, odmietnuté spojenie, TLS, mixed content alebo browser extension. [11]
- Skontrolujte presný frontend origin: schéma, host a port.
- Skontrolujte `OPTIONS`, ak existuje.
- Overte status a `Access-Control-Allow-*` preflight odpovede.
- Overte, že aj skutočná odpoveď obsahuje požadované CORS headers.
- Pri cookies skontrolujte `credentials`, `Access-Control-Allow-Credentials`, `SameSite` a `Secure`.
- Skontrolujte redirecty, TLS, mixed content a sieťové chyby.
- Porovnajte browser request s fungujúcim curl alebo API-tool requestom.
Nebezpečné CORS konfigurácie
Slepé vracanie prichádzajúceho `Origin` je nebezpečné, ak requesty s credentials vracajú citlivé dáta. Útočník môže ovládať vlastný origin a pokúsiť sa čítať odpovede v kontexte prihláseného používateľa. [16]
Príliš široké regulárne výrazy a dôvera všetkým subdoménam sú tiež rizikové, ak možno niektorú subdoménu prevziať. OWASP odporúča presný allowlist. [16][17]
`Access-Control-Allow-Origin: *` nie je automaticky zraniteľnosť pri úplne verejných dátach bez credentials. Problémom je politika širšia, než je skutočne potrebné. [8][17]
Výkon: cena preflightu a `Access-Control-Max-Age`
Preflight pridáva pred skutočný request ďalší `OPTIONS` a môže zvýšiť latenciu. Jeho výsledok možno cacheovať pomocou `Access-Control-Max-Age` v samostatnej preflight cache. [5][10]
MDN uvádza bez headeru predvolenú hodnotu 5 sekúnd. Prehliadače majú vlastné maximá. Firefox obmedzuje na 86400 sekúnd a Chromium od verzie 76 na 7200 sekúnd. [10]
Stabilné politiky možno cacheovať, ale nie tak dlho, aby bolo ťažké rýchlo odvolať omylom príliš široké povolenie.
Praktická konfigurácia a checklist nasadenia
Najprv rozhodnite, či endpoint má byť vôbec dostupný cross-origin. Ak nie, CORS headers nepridávajte. Ak áno, definujte allowlist originov, potrebné metódy a headers a potrebu credentials. [14][17]
Pri dynamickom allowliste vráťte iba overený origin a pridajte `Vary: Origin`. S credentials používajte konkrétny origin a `Access-Control-Allow-Credentials: true`. Frontend toto serverové rozhodnutie nemôže nahradiť. [1][2]
Po nasadení otestujte jednoduchý request, preflight, scenár s cookie, nepovolený origin a skutočné chyby v DevTools.
- Je cross-origin prístup naozaj potrebný?
- Ktoré presné originy majú byť povolené?
- Sú potrebné credentials?
- Ktoré metódy a request headers sú potrebné?
- Dostane sa `OPTIONS` do aplikácie a odpovie správne?
- Obsahuje skutočná odpoveď správne CORS headers?
- Obsahujú dynamické odpovede `Vary: Origin`?
- Vyžaduje endpoint stále bežnú autentizáciu a autorizáciu?
- Bola politika otestovaná s povoleným aj nepovoleným originom?

