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

Access-Control-Allow-Origin Wildcard: CORS Risk & How to Fix It

Setting Access-Control-Allow-Origin: * lets any site read your API responses. Learn exactly when the wildcard is dangerous and how to implement a secure CORS allow-list instead.

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

Access-Control-Allow-Origin: * is one of the most common CORS misconfigurations found in production APIs. It appears in security scan reports as CWE-942 (Permissive Cross-domain Policy with Untrusted Domains), and it comes up regularly in penetration testing findings.

This guide explains what the wildcard does, why it’s a problem, and how to configure CORS correctly for your use case.


What is CORS?

Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls which origins can make requests to your server. By default, browsers block cross-origin requests from JavaScript (the Same-Origin Policy). CORS headers let servers selectively relax that restriction.

When your frontend at https://app.example.com makes an API call to https://api.example.com, the browser checks:

  1. Does the API response include Access-Control-Allow-Origin: https://app.example.com?
  2. If yes, allow the response to be read by JavaScript.
  3. If no, block it.

The wildcard * tells browsers: any origin is allowed to read this response.


What does Access-Control-Allow-Origin: * actually allow?

The wildcard permits any website on the internet to:

  • Make GET and POST requests to your API from JavaScript
  • Read the response body — this is the critical part

Without the wildcard, an attacker’s page at https://evil.com could make a request to your API, but the browser would block the response before the attacker’s JavaScript could read it.

With *, the attacker’s JavaScript can read your API responses directly.


When is the wildcard actually dangerous?

Scenario 1: API returns sensitive data

// Attacker's page at evil.com
fetch('https://api.yourapp.com/user/profile', {
  credentials: 'include'  // sends cookies
})
.then(r => r.json())
.then(data => {
  // If CORS wildcard is set, attacker can read this
  exfiltrate(data.email, data.address, data.payment_info);
});

If your API returns user data based on session cookies and allows *, an attacker can trick a logged-in user into visiting evil.com and silently exfiltrate their data.

Important caveat: Access-Control-Allow-Origin: * cannot be combined with Access-Control-Allow-Credentials: true. If you try to use both, browsers ignore the wildcard and block the request. This limits the wildcard’s risk for cookie-authenticated endpoints — but not for token-based auth sent in headers.

Scenario 2: Bearer token in Authorization header

// Attacker's page
fetch('https://api.yourapp.com/admin/users', {
  headers: {
    'Authorization': 'Bearer ' + stolenToken
  }
})
.then(r => r.json())
.then(data => exfiltrate(data));

If the attacker has obtained a JWT or API key (via XSS, phishing, etc.) and your API allows *, they can now read responses from any origin — including through malicious browser extensions, compromised browser plugins, or injected scripts.

Scenario 3: Internal APIs exposed to the internet

Many developers set Access-Control-Allow-Origin: * on internal APIs expecting them to only be accessed from internal networks. When those APIs are inadvertently exposed to the internet, the wildcard becomes a real attack surface.


What the wildcard does NOT allow

It’s important to be precise about what * doesn’t do:

  • It does not allow cross-origin requests with cookies (credentials: 'include') — that requires explicit origin whitelisting
  • It does not bypass authentication — requests still need valid credentials
  • It does not allow non-simple requests (PUT, DELETE, custom headers) without a preflight response that also uses *

The wildcard is specifically risky for APIs that return sensitive data and are accessed with bearer tokens or without authentication.


How to fix CORS wildcard

Instead of *, specify exactly which origins are allowed:

// Node.js / Express example
const allowedOrigins = [
  'https://app.yourcompany.com',
  'https://admin.yourcompany.com',
];

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');  // important for caching
  }
  next();
});
# Django example (using django-cors-headers)
CORS_ALLOWED_ORIGINS = [
    "https://app.yourcompany.com",
    "https://admin.yourcompany.com",
]
# NOT: CORS_ALLOW_ALL_ORIGINS = True
// Spring Boot example
@Configuration
public class CorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                    .allowedOrigins(
                        "https://app.yourcompany.com",
                        "https://admin.yourcompany.com"
                    )
                    .allowedMethods("GET", "POST", "PUT", "DELETE")
                    .allowCredentials(true);
            }
        };
    }
}

Option 2: Dynamic origin validation

For more flexibility (e.g., allowing all subdomains of your domain):

function isAllowedOrigin(origin) {
  if (!origin) return false;
  try {
    const url = new URL(origin);
    // Allow any subdomain of yourcompany.com over HTTPS
    return url.protocol === 'https:' &&
           (url.hostname === 'yourcompany.com' ||
            url.hostname.endsWith('.yourcompany.com'));
  } catch {
    return false;
  }
}

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (isAllowedOrigin(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Vary', 'Origin');
  }
  next();
});

Always set Vary: Origin when dynamically reflecting origins — this prevents CDNs and proxies from caching a response with one origin’s header and serving it to a different origin.

Option 3: Wildcard is acceptable for truly public APIs

If your API intentionally serves public data (weather APIs, public datasets, open documentation endpoints), * is fine. The risk is only present when the API returns user-specific or sensitive data.

Examples where * is appropriate:

  • Public read-only REST APIs (e.g., GET /api/v1/blog/posts)
  • Font servers, CDN endpoints
  • Public analytics ingestion endpoints (receiving data, not returning sensitive data)

How scanners detect CORS wildcard

SAST tools detect * wildcards in CORS configuration code:

# Python — SAST flags this
response.headers['Access-Control-Allow-Origin'] = '*'

# SAST flags this too
app.config['CORS_ORIGINS'] = '*'

DAST tools detect it by making a cross-origin test request and inspecting the response headers:

curl -H "Origin: https://evil.com" -I https://api.example.com/endpoint
# If response contains: Access-Control-Allow-Origin: *
# → CORS wildcard misconfiguration found

Offensive360’s DAST scanner checks CORS configuration on every endpoint during a scan and flags wildcard usage when sensitive data is detected in responses.


CORS wildcard vs other CORS misconfigurations

The wildcard is actually one of the simpler CORS issues. More dangerous patterns include:

MisconfigurationRiskExample
Allow-Origin: *MediumAny site reads public data
Reflecting arbitrary OriginHighif origin: set allow-origin = origin — no validation
Trusting null originHighSandboxed iframes bypass origin checks
Subdomain wildcard regex flawHighevil.com matches .*evil.comnotevil.com passes
Trusting http:// on HTTPS siteMediumDowngrade attack surface

The most dangerous pattern is reflecting the Origin header without validation — it effectively gives every website the same access as *, but also works with Access-Control-Allow-Credentials: true.


Offensive360 DAST scans automatically test CORS configuration on every endpoint, flagging wildcard usage, origin reflection without validation, and credential exposure. Run a scan on your web application for $500.

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.