Een diepgaande verkenning van Cross-Origin Resource Sharing (CORS) en preflight requests. Leer hoe u CORS-problemen kunt oplossen en uw webapplicaties kunt beveiligen voor een wereldwijd publiek.
CORS ontrafeld: Een diepgaande duik in JavaScript Preflight Request Handling
In de steeds groter wordende wereld van webontwikkeling is beveiliging van het grootste belang. Cross-Origin Resource Sharing (CORS) is een cruciaal beveiligingsmechanisme dat door webbrowsers wordt geïmplementeerd om te voorkomen dat webpagina's verzoeken indienen bij een ander domein dan het domein dat de webpagina heeft geleverd. Dit is een fundamentele beveiligingsfunctie die is ontworpen om te voorkomen dat kwaadwillende websites toegang krijgen tot gevoelige gegevens. Deze uitgebreide gids gaat dieper in op de complexiteit van CORS, met bijzondere aandacht voor de preflight request handling. We zullen de 'waarom', 'wat' en 'hoe' van CORS onderzoeken en praktische voorbeelden en oplossingen bieden voor veelvoorkomende problemen die ontwikkelaars wereldwijd tegenkomen.
Inzicht in het Same-Origin Policy
De kern van CORS wordt gevormd door het Same-Origin Policy (SOP). Dit beleid is een beveiligingsmechanisme op browserniveau dat scripts die op een origin worden uitgevoerd, beperkt tot toegang tot bronnen van een andere origin. Een origin wordt gedefinieerd door het protocol (bijv. HTTP of HTTPS), domein (bijv. example.com) en poort (bijv. 80 of 443). Twee URL's hebben dezelfde origin als deze drie componenten exact overeenkomen.
Bijvoorbeeld:
https://www.example.com/app1/index.htmlenhttps://www.example.com/app2/index.htmlhebben dezelfde origin (hetzelfde protocol, domein en poort).https://www.example.com/index.htmlenhttp://www.example.com/index.htmlhebben verschillende origins (verschillende protocollen).https://www.example.com/index.htmlenhttps://api.example.com/index.htmlhebben verschillende origins (verschillende subdomeinen worden als verschillende domeinen beschouwd).https://www.example.com:8080/index.htmlenhttps://www.example.com/index.htmlhebben verschillende origins (verschillende poorten).
Het SOP is ontworpen om te voorkomen dat kwaadwillende scripts op de ene website toegang krijgen tot gevoelige gegevens, zoals cookies of gebruikersauthenticatiegegevens, op een andere website. Hoewel essentieel voor de beveiliging, kan het SOP ook beperkend zijn, vooral wanneer legitieme cross-origin requests nodig zijn.
Wat is Cross-Origin Resource Sharing (CORS)?
CORS is een mechanisme waarmee servers kunnen specificeren welke origins (domeinen, schema's of poorten) toegang hebben tot hun bronnen. Het versoepelt in wezen het SOP, waardoor gecontroleerde cross-origin toegang mogelijk is. CORS wordt geïmplementeerd met behulp van HTTP-headers die worden uitgewisseld tussen de client (meestal een webbrowser) en de server.
Wanneer een browser een cross-origin request indient (d.w.z. een request naar een andere origin dan de huidige pagina), controleert hij eerst of de server het request toestaat. Dit gebeurt door de Access-Control-Allow-Origin header in het antwoord van de server te onderzoeken. Als de origin van het request in deze header staat (of als de header is ingesteld op *, waardoor alle origins zijn toegestaan), staat de browser toe dat het request doorgaat. Anders blokkeert de browser het request, waardoor de JavaScript-code geen toegang heeft tot de antwoordgegevens.
De rol van Preflight Requests
Voor bepaalde soorten cross-origin requests initieert de browser een preflight request. Dit is een OPTIONS request dat naar de server wordt verzonden vóór het eigenlijke request. Het doel van het preflight request is om te bepalen of de server bereid is om het daadwerkelijke request te accepteren. De server reageert op het preflight request met informatie over de toegestane methoden, headers en andere beperkingen.
Preflight requests worden geactiveerd wanneer het cross-origin request aan een van de volgende voorwaarden voldoet:
- De requestmethode is niet
GET,HEADofPOST. - Het request bevat aangepaste headers (d.w.z. headers anders dan degene die automatisch door de browser worden toegevoegd).
- De
Content-Typeheader is ingesteld op iets anders danapplication/x-www-form-urlencoded,multipart/form-dataoftext/plain. - Het request gebruikt
ReadableStreamobjecten in de body.
Een PUT request met een Content-Type van application/json activeert bijvoorbeeld een preflight request omdat het een andere methode gebruikt dan de toegestane en een mogelijk niet-toegestaan contenttype.
Waarom Preflight Requests?
Preflight requests zijn essentieel voor de beveiliging omdat ze de server de mogelijkheid bieden om potentieel schadelijke cross-origin requests af te wijzen voordat ze worden uitgevoerd. Zonder preflight requests zou een kwaadwillende website mogelijk willekeurige requests naar een server kunnen verzenden zonder de expliciete toestemming van de server. Een preflight request stelt de server in staat om te valideren dat het request acceptabel is en voorkomt potentieel schadelijke bewerkingen.
Preflight Requests aan de serverzijde afhandelen
Het correct afhandelen van preflight requests is cruciaal om ervoor te zorgen dat uw webapplicatie correct en veilig functioneert. De server moet reageren op het OPTIONS request met de juiste CORS-headers om aan te geven of het daadwerkelijke request is toegestaan.
Hier volgt een overzicht van de belangrijkste CORS-headers die worden gebruikt in preflight-antwoorden:
Access-Control-Allow-Origin: deze header specificeert de origin(s) die toegang hebben tot de resource. Deze kan worden ingesteld op een specifieke origin (bijv.https://www.example.com) of op*om alle origins toe te staan. Het gebruik van*wordt echter over het algemeen afgeraden om veiligheidsredenen, vooral als de server gevoelige gegevens verwerkt.Access-Control-Allow-Methods: deze header specificeert de HTTP-methoden die zijn toegestaan voor het cross-origin request (bijv.GET,POST,PUT,DELETE).Access-Control-Allow-Headers: deze header specificeert de lijst met niet-standaard HTTP-headers die zijn toegestaan in het daadwerkelijke request. Dit is nodig als de client aangepaste headers verzendt, zoalsX-Custom-HeaderofAuthorization.Access-Control-Allow-Credentials: deze header geeft aan of het daadwerkelijke request credentials kan bevatten, zoals cookies of authorization headers. Deze moet worden ingesteld optrueals de client-side code credentials verzendt en de server deze moet accepteren. Opmerking: wanneer deze header is ingesteld op `true`, kan `Access-Control-Allow-Origin` *niet* worden ingesteld op `*`. U moet de origin specificeren.Access-Control-Max-Age: deze header specificeert de maximale hoeveelheid tijd (in seconden) dat de browser het preflight-antwoord kan cachen. Dit kan de prestaties helpen verbeteren door het aantal preflight requests dat wordt verzonden te verminderen.
Voorbeeld: Preflight Requests afhandelen in Node.js met Express
Hier is een voorbeeld van hoe u preflight requests kunt afhandelen in een Node.js-applicatie met behulp van het Express-framework:
const express = require('express');
const cors = require('cors');
const app = express();
// Enable CORS for all origins (for development purposes only!)
// In production, specify allowed origins for better security.
app.use(cors()); //or app.use(cors({origin: 'https://www.example.com'}));
// Route for handling OPTIONS requests (preflight)
app.options('/data', cors()); // Enable CORS for a single route. Or specify origin: cors({origin: 'https://www.example.com'})
// Route for handling GET requests
app.get('/data', (req, res) => {
res.json({ message: 'This is cross-origin data!' });
});
// Route to handle a preflight and a post request
app.options('/resource', cors()); // enable pre-flight request for DELETE request
app.delete('/resource', cors(), (req, res, next) => {
res.send('delete resource')
})
const port = 3000;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
In dit voorbeeld gebruiken we de cors middleware om CORS-requests af te handelen. Voor meer gedetailleerde controle kan CORS worden ingeschakeld per route. Opmerking: in productie wordt ten zeerste aanbevolen om de toegestane origins te specificeren met behulp van de origin optie in plaats van alle origins toe te staan. Het toestaan van alle origins met behulp van * kan uw applicatie blootstellen aan beveiligingsproblemen.
Voorbeeld: Preflight Requests afhandelen in Python met Flask
Hier is een voorbeeld van hoe u preflight requests kunt afhandelen in een Python-applicatie met behulp van het Flask-framework en de flask_cors extensie:
from flask import Flask, jsonify
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app) # Enable CORS for all routes
@app.route('/data')
@cross_origin()
def get_data():
data = {"message": "This is cross-origin data!"}
return jsonify(data)
if __name__ == '__main__':
app.run(debug=True)
Dit is het eenvoudigste gebruik. Zoals eerder kunnen de origins worden beperkt. Zie de flask-cors documentatie voor meer informatie.
Voorbeeld: Preflight Requests afhandelen in Java met Spring Boot
Hier is een voorbeeld van hoe u preflight requests kunt afhandelen in een Java-applicatie met behulp van Spring Boot:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class CorsApplication {
public static void main(String[] args) {
SpringApplication.run(CorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/data").allowedOrigins("http://localhost:8080");
}
};
}
}
En de bijbehorende controller:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DataController {
@GetMapping("/data")
public String getData() {
return "This is cross-origin data!";
}
}
Veelvoorkomende CORS-problemen en -oplossingen
Ondanks het belang ervan kan CORS vaak een bron van frustratie zijn voor ontwikkelaars. Hier zijn enkele veelvoorkomende CORS-problemen en hun oplossingen:
-
Fout: "No 'Access-Control-Allow-Origin' header is present on the requested resource."
Deze fout geeft aan dat de server de
Access-Control-Allow-Originheader niet retourneert in zijn antwoord. Om dit op te lossen, moet u ervoor zorgen dat de server is geconfigureerd om de header op te nemen en dat deze is ingesteld op de juiste origin of op*(indien van toepassing).Oplossing: Configureer de server om de `Access-Control-Allow-Origin` header in het antwoord op te nemen en stel deze in op de origin van de aanvragende website of op `*` om alle origins toe te staan (gebruik met voorzichtigheid).
-
Fout: "Response to preflight request doesn't pass access control check: Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers in preflight response."
Deze fout geeft aan dat de server de aangepaste header (
X-Custom-Headerin dit voorbeeld) niet toestaat in het cross-origin request. Om dit op te lossen, moet u ervoor zorgen dat de server de header opneemt in deAccess-Control-Allow-Headersheader in het preflight-antwoord.Oplossing: Voeg de aangepaste header (bijv. `X-Custom-Header`) toe aan de `Access-Control-Allow-Headers` header in het preflight-antwoord van de server.
-
Fout: "Credentials flag is 'true', but the 'Access-Control-Allow-Origin' header is '*'."
Wanneer de
Access-Control-Allow-Credentialsheader is ingesteld optrue, moet deAccess-Control-Allow-Originheader worden ingesteld op een specifieke origin, niet op*. Dit komt omdat het toestaan van credentials van alle origins een veiligheidsrisico zou vormen.Oplossing: Wanneer u credentials gebruikt, stelt u `Access-Control-Allow-Origin` in op een specifieke origin in plaats van `*`.
-
Preflight request wordt niet verzonden.
Controleer nogmaals of uw Javascript-code de eigenschap `credentials: 'include'` bevat. Controleer ook of uw server `Access-Control-Allow-Credentials: true` toestaat.
-
Conflicterende configuraties tussen server en client.
Controleer zorgvuldig uw server-side CORS-configuratie naast de client-side instellingen. Mismatches (bijv. server staat alleen GET requests toe, maar client verzendt POST) veroorzaken CORS-fouten.
CORS en best practices voor beveiliging
Hoewel CORS gecontroleerde cross-origin toegang mogelijk maakt, is het essentieel om best practices voor beveiliging te volgen om kwetsbaarheden te voorkomen:
- Vermijd het gebruik van
*in deAccess-Control-Allow-Originheader in productie. Hierdoor kunnen alle origins toegang krijgen tot uw resources, wat een veiligheidsrisico kan zijn. Specificeer in plaats daarvan de exacte origins die zijn toegestaan. - Overweeg zorgvuldig welke methoden en headers u wilt toestaan. Sta alleen de methoden en headers toe die strikt noodzakelijk zijn voor het correct functioneren van uw applicatie.
- Implementeer de juiste authenticatie- en autorisatiemechanismen. CORS is geen vervanging voor authenticatie en autorisatie. Zorg ervoor dat uw API wordt beschermd door passende beveiligingsmaatregelen.
- Valideer en zuiver alle gebruikersinvoer. Dit helpt cross-site scripting (XSS)-aanvallen en andere kwetsbaarheden te voorkomen.
- Houd uw server-side CORS-configuratie up-to-date. Controleer en update uw CORS-configuratie regelmatig om ervoor te zorgen dat deze overeenkomt met de beveiligingseisen van uw applicatie.
CORS in verschillende ontwikkelomgevingen
CORS-problemen kunnen zich verschillend manifesteren in verschillende ontwikkelomgevingen en technologieën. Hier is een overzicht van hoe u CORS in een paar veelvoorkomende scenario's kunt benaderen:
Lokale ontwikkelomgevingen
Tijdens de lokale ontwikkeling kunnen CORS-problemen bijzonder vervelend zijn. Browsers blokkeren vaak requests van uw lokale ontwikkelserver (bijv. localhost:3000) naar een externe API. Verschillende technieken kunnen deze pijn verlichten:
- Browser Extensies: Extensies zoals "Allow CORS: Access-Control-Allow-Origin" kunnen tijdelijk CORS-beperkingen uitschakelen voor testdoeleinden. Gebruik deze echter *nooit* in productie.
- Proxy Servers: Configureer een proxy server die requests van uw lokale ontwikkelserver doorstuurt naar de externe API. Dit maakt de requests in feite "same-origin" vanuit het perspectief van de browser. Tools zoals
http-proxy-middleware(voor Node.js) zijn hiervoor handig. - Configureer Server CORS: Zelfs tijdens de ontwikkeling is het best practice om uw API-server te configureren om expliciet requests van uw lokale ontwikkelorigin toe te staan (bijv.
http://localhost:3000). Dit simuleert een real-world CORS-configuratie en helpt u vroegtijdig problemen te ontdekken.
Serverless omgevingen (bijv. AWS Lambda, Google Cloud Functions)
Serverless functies vereisen vaak een zorgvuldige CORS-configuratie. Veel serverless platforms bieden ingebouwde CORS-ondersteuning, maar het is cruciaal om deze correct te configureren:
- Platform-specifieke instellingen: Gebruik de ingebouwde CORS-configuratieopties van het platform. AWS Lambda staat u bijvoorbeeld toe om toegestane origins, methoden en headers rechtstreeks in de API Gateway instellingen te specificeren.
- Middleware/Libraries: Voor meer flexibiliteit kunt u middleware of libraries gebruiken om CORS binnen uw serverless functie code af te handelen. Dit is vergelijkbaar met de benaderingen die worden gebruikt in traditionele serveromgevingen (bijv. het gebruik van het `cors` pakket in Node.js Lambda functies).
- Overweeg de `OPTIONS` methode: Zorg ervoor dat uw serverless functie `OPTIONS` requests correct afhandelt. Dit omvat vaak het maken van een afzonderlijke route die de juiste CORS-headers retourneert.
Mobile App Development (bijv. React Native, Flutter)
CORS is minder een direct probleem voor native mobiele apps (Android, iOS), omdat ze het same-origin policy doorgaans niet op dezelfde manier afdwingen als webbrowsers. CORS kan echter nog steeds relevant zijn als uw mobiele app een webweergave gebruikt om web content weer te geven of als u frameworks zoals React Native of Flutter gebruikt die gebruikmaken van JavaScript:
- Web Views: Als uw mobiele app een webweergave gebruikt om web content weer te geven, zijn dezelfde CORS-regels van toepassing als in een webbrowser. Configureer uw server om requests van de origin van de web content toe te staan.
- React Native/Flutter: Deze frameworks gebruiken JavaScript om API-requests te doen. Hoewel de native omgeving CORS mogelijk niet direct afdwingt, kunnen de onderliggende HTTP-clients (bijv. `fetch`) in bepaalde situaties nog steeds CORS-achtig gedrag vertonen.
- Native HTTP Clients: Wanneer u API-requests rechtstreeks vanuit native code doet (bijv. met behulp van OkHttp op Android of URLSession op iOS), is CORS over het algemeen geen factor. U moet echter nog steeds best practices voor beveiliging overwegen, zoals de juiste authenticatie en autorisatie.
Globale overwegingen voor CORS-configuratie
Bij het configureren van CORS voor een wereldwijd toegankelijke applicatie is het cruciaal om rekening te houden met factoren zoals:
- Data Sovereignty: Regelgeving in sommige regio's schrijft voor dat gegevens zich binnen de regio bevinden. CORS kan betrokken zijn bij het openen van bronnen over de grenzen heen, waardoor mogelijk datalocatie wetten worden overtreden.
- Regional Security Policies: Verschillende landen kunnen verschillende cyberbeveiligingsvoorschriften en richtlijnen hebben die van invloed zijn op de manier waarop CORS moet worden geïmplementeerd en beveiligd.
- Content Delivery Networks (CDN's): Zorg ervoor dat uw CDN correct is geconfigureerd om de noodzakelijke CORS-headers door te geven. Onjuist geconfigureerde CDN's kunnen CORS-headers verwijderen, wat tot onverwachte fouten kan leiden.
- Load Balancers en Proxies: Controleer of load balancers of reverse proxies in uw infrastructuur preflight-requests correct afhandelen en CORS-headers doorgeven.
- Multilingual Support: Overweeg hoe CORS interageert met de internationaliserings- (i18n) en lokalisatiestrategieën (l10n) van uw applicatie. Zorg ervoor dat CORS-beleid consistent is in verschillende taalversies van uw applicatie.
CORS testen en debuggen
Effectief testen en debuggen van CORS is essentieel. Hier zijn enkele technieken:
- Browser Developer Tools: De developer console van de browser is uw eerste stop. Het "Network"-tabblad toont preflight-requests en de responses, waardoor zichtbaar wordt of CORS-headers aanwezig en correct geconfigureerd zijn.
- `curl` Command-Line Tool: Gebruik `curl -v -X OPTIONS
` om handmatig preflight-requests te versturen en de response headers van de server te inspecteren. - Online CORS Checkers: Talrijke online tools kunnen helpen bij het valideren van uw CORS-configuratie. Zoek gewoon naar "CORS checker".
- Unit en Integration Tests: Schrijf geautomatiseerde tests om te controleren of uw CORS-configuratie werkt zoals verwacht. Deze tests moeten zowel succesvolle cross-origin requests als scenario's omvatten waarin CORS de toegang moet blokkeren.
- Logging en Monitoring: Implementeer logging om CORS-gerelateerde gebeurtenissen bij te houden, zoals preflight-requests en geblokkeerde requests. Bewaak uw logs op verdachte activiteiten of configuratiefouten.
Conclusie
Cross-Origin Resource Sharing (CORS) is een essentieel beveiligingsmechanisme dat gecontroleerde cross-origin toegang tot webresources mogelijk maakt. Het begrijpen van hoe CORS werkt, vooral preflight requests, is cruciaal voor het bouwen van veilige en betrouwbare webapplicaties. Door de best practices te volgen die in deze handleiding worden beschreven, kunt u CORS-problemen effectief aanpakken en uw applicatie beschermen tegen potentiële kwetsbaarheden. Vergeet niet om altijd prioriteit te geven aan beveiliging en de implicaties van uw CORS-configuratie zorgvuldig te overwegen.
Naarmate de webontwikkeling evolueert, zal CORS een cruciaal aspect van webbeveiliging blijven. Op de hoogte blijven van de nieuwste CORS best practices en technieken is essentieel voor het bouwen van veilige en wereldwijd toegankelijke webapplicaties.