Overly permissive CORS regular expressions

Overly permissive CORS regular expressions with wildcards or unescaped dots

Description

An unescaped dot (.) in a regular expression used to allow CORS (Cross-Origin Resource Sharing) origins can admit a much wider set of domains than intended. JavaScript from an unintended origin may then be allowed to read responses. Exposure of sensitive responses depends on their content, whether credentials are sent, and the server's CORS settings. CORS does not prevent every request from being sent.

Potential impact

  • An incorrect expression can allow scripts from unintended origins to read responses.
  • An attacker-controlled domain may gain access to personal or other sensitive data.
  • A malicious site may read sensitive authenticated responses when credentials are allowed.

Remediation

  • Escape literal dots in domain patterns as \..
  • Configure CORS to allow only specific trusted origins.
  • Avoid wildcards (*) and overly broad patterns.
  • Test CORS changes to confirm that the accepted origins stay within the intended scope.

Examples

Before

tsx
const corsDomains = [
  /(.+\.)*example.com$/,
  /^(http|https):\/\/.+\.evil\.com$/,
  /^(http|https):\/\/(.+)\.my\.site.com$/,
];

// Example CORS configuration
app.use(cors({
  origin: corsDomains,
}));

After

tsx
const corsAllowedDomains = [
  /^https?:\/\/www\.example\.com$/,   // Allow only 'www.example.com'
  /^https?:\/\/service\.my\.site\.com$/,  // Allow only the service subdomain
];

app.use(cors({
  origin: corsAllowedDomains,
}));

Explanation:

  • Before: The first pattern has an unescaped dot and no start boundary, so it may accept lookalike hosts. Review the broad subdomain ranges in the other patterns as well.
  • After: Start and end boundaries and escaped dots limit the list to HTTP or HTTPS origins on the two hosts. Also verify origin ownership and whether production must require HTTPS.

References