Cross-Site Data Access Blocked — But Your Server Is Misconfigured

Your server is sending two contradictory security instructions at the same time — one that says 'anyone on the internet can read our responses' and another that says 'include the user's private login credentials.' Browsers are smart enough to refuse this combination, so no one is being harmed right now. But this configuration signals a deeper misunderstanding of how cross-site access controls work, and a developer trying to 'fix' it the wrong way could accidentally create a real vulnerability.

Business Impact And Actions

medium urgency

Business Impact

As-is, this misconfiguration is blocked by browsers and poses no immediate risk to your customers or their data. However, it may flag as a finding in compliance audits (such as SOC 2 or penetration tests commissioned by enterprise customers), and it indicates your server's cross-site access rules need a proper review. If a developer patches this incorrectly — a common mistake — it could open a genuine data-access vulnerability.

What To Do

  1. Share this report with your developer and ask them to review how your server decides which websites are allowed to make requests to your API.
  2. Ask your developer to replace the 'allow everyone' wildcard setting with an explicit list of the specific domains that genuinely need access (e.g., your own app's domain).
  3. If your API is truly public and doesn't use login sessions or cookies, ask your developer to remove the credentials header entirely — it's unnecessary.
  4. After the fix, ask your developer to verify the change using the browser's developer tools or a free online CORS checker.

CORS Misconfiguration: Access-Control-Allow-Origin: * Combined with Access-Control-Allow-Credentials: true

medium severity CVSS 4.0-5.0

Vulnerability Explanation

The server responds with both `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Credentials: true` simultaneously. The CORS specification explicitly forbids this combination — browsers will reject the response and block the cross-origin request. While this means the exact header pair is not directly exploitable in modern browsers, it is a strong indicator of a flawed CORS implementation. The most common developer 'fix' — switching from a static wildcard to dynamic origin reflection (echoing back whatever Origin header the client sends) — produces a configuration that IS exploitable: any attacker-controlled site can make credentialed cross-origin requests and read the authenticated response.

Root Cause

Developers often set `Access-Control-Allow-Credentials: true` to support authenticated cross-origin requests, then set `Access-Control-Allow-Origin: *` for convenience or to silence browser CORS errors. When browsers reject this combination, the tempting next step is to dynamically reflect the incoming `Origin` header — which is functionally equivalent to a wildcard but bypasses the browser's built-in protection. The root cause is a missing explicit allowlist of trusted origins.

Technical Impact

In its current form (static wildcard + credentials), browsers block the response — no credential leakage occurs. If the configuration is 'fixed' by switching to dynamic origin reflection, an attacker can make authenticated cross-origin requests from any domain, potentially reading session-authenticated API responses, exfiltrating user data, or performing actions on behalf of logged-in users. The risk is also elevated if the application relies on network-level access controls (VPN, IP allowlisting) rather than credentials, since wildcard CORS bypasses those assumptions.

Severity Justification

The exact wildcard+credentials combination is blocked by all modern browsers per the CORS spec, making it non-exploitable as-is. Severity is medium because it represents a confirmed misconfiguration that is one developer mistake away from becoming a high-severity exploitable CORS issue (origin reflection), and it may indicate other CORS misconfigurations exist in the same codebase.

Affected Components

  • HTTP server CORS configuration — all versions where both headers are set simultaneously

Remediation Steps

  1. Identify all endpoints returning both `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Credentials: true`. Search your codebase and server config for both headers co-occurring.
  2. If the endpoint serves authenticated users: replace the wildcard with an explicit allowlist of trusted origins. Validate the incoming `Origin` header against this list using exact string matching — never substring or regex matching.
  3. If the endpoint is a public API that requires no authentication: remove `Access-Control-Allow-Credentials: true` entirely (or set it to `false`). A wildcard origin is acceptable for fully public, unauthenticated endpoints.
  4. Ensure your origin validation logic uses strict equality, not `.includes()`, `.startsWith()`, or regex patterns — these are commonly bypassed (e.g., `evil.com?trusted.com`, `trusted.com.evil.com`).
  5. Deploy the fix and verify using `curl` and browser DevTools (see verification steps).

Verification Steps

  1. Run: `curl -H 'Origin: https://evil.example.com' -I https://your-api.com/endpoint` — the response should NOT echo back `evil.example.com` in `Access-Control-Allow-Origin`.
  2. Run: `curl -H 'Origin: https://your-trusted-app.com' -I https://your-api.com/endpoint` — the response SHOULD return `Access-Control-Allow-Origin: https://your-trusted-app.com` and `Access-Control-Allow-Credentials: true`.
  3. Confirm the wildcard `*` no longer appears alongside `Access-Control-Allow-Credentials: true` in any response.
  4. Use the browser DevTools Network tab to confirm cross-origin requests from your legitimate frontend succeed, and that requests from an untrusted origin are blocked.

Code Examples (javascript)

Vulnerable
// VULNERABLE: Wildcard with credentials (blocked by browsers)
// Also vulnerable: dynamic reflection without validation
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*'); // or: req.headers.origin
  res.header('Access-Control-Allow-Credentials', 'true');
  next();
});

// Also vulnerable: using cors() with origin: true (reflects any origin)
app.use(cors({ origin: true, credentials: true }));
Fixed
// SECURE: Explicit allowlist with exact-match validation
const ALLOWED_ORIGINS = [
  'https://app.example.com',
  'https://admin.example.com',
];

app.use(cors({
  origin: (origin, callback) => {
    // Allow server-to-server requests (no Origin header)
    if (!origin) return callback(null, true);
    if (ALLOWED_ORIGINS.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true, // Safe because origin is strictly validated
}));

// For Nginx:
# map $http_origin $cors_origin {
#   default "";
#   "https://app.example.com" $http_origin;
#   "https://admin.example.com" $http_origin;
# }
# add_header Access-Control-Allow-Origin $cors_origin;
# add_header Access-Control-Allow-Credentials true;

Best Practices

  • Always maintain an explicit server-side allowlist of trusted origins; never derive the allowed origin dynamically from the request without validating against this list.
  • Only set `Access-Control-Allow-Credentials: true` on endpoints that genuinely require cookie- or token-based authentication in cross-origin contexts — omit it everywhere else.
  • Use exact string matching for origin validation; avoid substring, prefix, or regex checks which are frequently bypassable.
  • Remember that CORS is a browser-enforced control only — it does not protect server-to-server requests or non-browser clients. Always enforce authentication and authorization server-side regardless of CORS configuration.

Found this in your infrastructure?

VulWall scans for this and dozens of other issues automatically.

Scan Your Domain Free