Wat is CORS en hoe werkt het in je browser?
Auteur: Redactie Domeny.ForSale Gepubliceerd
Kort samengevat: CORS (Cross-Origin Resource Sharing) is een mechanisme waarmee een server de browser toestemming kan geven om een antwoord beschikbaar te stellen aan een script met een andere oorsprong. De antwoordheaders van de server bepalen of dat mag; de browser handhaaft die toestemming. Een CORS-fout betekent dus meestal dat het script het antwoord niet mag lezen, niet noodzakelijk dat er geen verbinding met de server is.
Waarom de browser toegang tussen verschillende oorsprongen beperkt
Browsers passen de same-origin policy toe: het beleid voor dezelfde oorsprong. Dit beperkt de mogelijkheid van een script om gegevens van een andere oorsprong te lezen. Zo kan een pagina in het ene tabblad niet zomaar privégegevens ophalen en lezen van andere diensten waarbij de gebruiker is ingelogd.
De oorsprong (origin) bestaat uit drie elementen: het protocol, de hostnaam en de poort. Zodra één daarvan verandert, is er sprake van een andere oorsprong. Het pad achter de hostnaam speelt hierbij geen rol.
Voor de pagina https://winkel.example ziet de vergelijking er zo uit:
| Doeladres | Dezelfde oorsprong? | Reden |
|---|---|---|
https://winkel.example/producten | Ja | Alleen het pad verandert |
https://api.winkel.example | Nee | Andere hostnaam |
http://winkel.example | Nee | Ander protocol |
https://sklep.example:8443 | Nee | Andere poort dan de standaardpoort 443 |
Met CORS kan de server deze regel gecontroleerd versoepelen. Het is geen HTTP-methode zoals GET of POST, maar een mechanisme dat op HTTP-headers berust.
Eenvoudig voorbeeld: een webwinkel haalt een productlijst op
Stel dat de website op https://winkel.example draait en de API, de interface die gegevens van de applicatie beschikbaar stelt, op https://api.winkel.example. Het script van de pagina wil producten ophalen:
fetch('https://api.sklep.example/produkty')
.then(antwoord => antwoord.json())
.then(producten => console.log(producten));
Omdat de adressen verschillende oorsprongen hebben, voegt de browser een header aan het verzoek toe die de oorsprong van de pagina aangeeft:
Origin: https://sklep.example
De API-server kan antwoorden met gegevens en een header die toestemming geeft:
Access-Control-Allow-Origin: https://sklep.example
De browser vergelijkt deze waarde met de oorsprong van de pagina. Komt de toestemming overeen, dan stelt de browser het antwoord beschikbaar aan het script. Ontbreekt de header of vermeldt deze een ander adres, dan kan het script het antwoord niet lezen — zelfs als de server de status 200 OK terugstuurt.
De header met toestemming moet van de API-server komen. Access-Control-Allow-Origin toevoegen aan de verzoekcode in de browser lost het probleem niet op.
Wanneer de browser eerst OPTIONS verstuurt
Niet elk verzoek naar een andere oorsprong wordt meteen verstuurd. Bij sommige verzoeken voert de browser eerst een preflight uit: een voorafgaand verzoek om te controleren of de server toestemming geeft.
Dit gebeurt bijvoorbeeld wanneer je een bestelling verstuurt met de methode POST en het inhoudstype application/json. Ook het gebruik van de header Authorization leidt tot zo’n controle. Het voorafgaande verzoek kan er als volgt uitzien:
OPTIONS /bestellingen HTTP/1.1
Origin: https://sklep.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
De server moet antwoorden met toestemming voor de oorsprong, de methode en de benodigde headers:
Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
Pas na een geslaagde controle verstuurt de browser het eigenlijke POST-verzoek. Het antwoord op dat POST-verzoek moet ook de juiste Access-Control-Allow-Origin-header bevatten.
Niet elk POST-verzoek vereist een preflight. Ook de headers en het type van de verstuurde inhoud spelen een rol; een gewoon GET-verzoek zonder extra headers heeft vaak geen voorafgaande controle nodig.
Cookies en het sterretje: twee belangrijke valkuilen
Een openbare API waarvan de antwoorden beschikbaar moeten zijn voor scripts van elke oorsprong, kan het volgende terugsturen:
Access-Control-Allow-Origin: *
Het sterretje is echter geen geschikte oplossing voor verzoeken die cookies meesturen in een modus waarin authenticatiegegevens worden opgenomen. In dat geval kan de code credentials: 'include' gebruiken. De server moet dan een specifieke toegestane oorsprong vermelden en het volgende terugsturen:
Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Credentials: true
De CORS-configuratie alleen garandeert niet dat cookies worden meegestuurd. Ook de cookieregels, waaronder SameSite, en de privacybeperkingen van de browser zijn van toepassing.
Als de server meerdere vertrouwde oorsprongen ondersteunt, moet hij de Origin-header controleren aan de hand van een lijst met toegestane waarden. Elke ontvangen oorsprong zonder controle terugsturen met toestemming voor authenticatiegegevens kan privéantwoorden blootstellen. Als het antwoord afhankelijk is van Origin, moet je ook zorgen voor Vary: Origin, zodat de cache onderscheid maakt tussen de verschillende antwoordvarianten.
Een CORS-fout onderzoeken
Open het netwerktabblad in de ontwikkelaarstools van de browser en voer vier controles uit:
- Vergelijk het protocol, de host en de poort van de pagina met die van de API.
- Controleer of er een
OPTIONS-verzoek is en of de server daar correct op antwoordt. - Bekijk de antwoordheaders van het eigenlijke verzoek, ook bij een serverfout.
- Controleer of de toestemming de gebruikte methode, headers en eventuele authenticatiegegevens omvat.
Dat een aanroep in curl werkt, bewijst niet dat CORS correct is ingesteld: zo’n tool handhaaft de beperkingen van de browser niet. Ook de instelling mode: 'no-cors' maakt het lezen van JSON niet mogelijk — die levert een ondoorzichtig antwoord op waarvan de inhoud niet beschikbaar is voor het script.
CORS vervangt geen inlogprocedure, toegangscontrole of bescherming tegen CSRF, waarbij een andere website ongewenste acties laat uitvoeren. Sommige verzoeken kunnen de server bereiken, ook als het lezen van het antwoord wordt geblokkeerd. Begin bij het oplossen van het probleem met de configuratie van de API of van een tussenliggende server die je beheert, niet met het uitschakelen van de browserbeveiliging.
Zie ook: Wat is een API?
