Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Handle cross-origin requests securely

Use CORS correctly, validate message origins with postMessage, and understand the Same-Origin Policy to prevent cross-origin attacks.

Utilities
Quick take
Typical fix time 20 min
  • Always verify event.origin before processing postMessage data
  • Never set Access-Control-Allow-Origin: * for authenticated endpoints
  • Validate and sanitize all data received from cross-origin messages
  • Use SameSite cookies to prevent CSRF from cross-origin requests
  • Reject unvalidated redirect targets such as `next`, `redirect`, or `callbackUrl`
Why it matters: The Same-Origin Policy is the browser's primary defense against malicious sites stealing data or performing actions on behalf of your users. Misconfiguring CORS or failing to validate postMessage origins can allow attackers to bypass this protection — reading private data, submitting forms as users, or injecting content into your pages.

Rule Details

Cross-origin communication is common in modern web apps — embedded iframes, third-party widgets, and cross-subdomain messaging all require it. Each mechanism has specific security requirements.

Code Example

// ❌ Dangerous: accepts messages from any origin
window.addEventListener('message', (event) => {
  const data = JSON.parse(event.data)
  processCommand(data.command) // Attacker's page can send any command!
})
 
// ✅ Always validate the origin
const ALLOWED_ORIGINS = ['https://app.example.com', 'https://admin.example.com']
 
window.addEventListener('message', (event) => {
  if (!ALLOWED_ORIGINS.includes(event.origin)) {
    console.warn('Rejected message from:', event.origin)
    return
  }
 
  let data
  try {
    data = JSON.parse(event.data)
  } catch {
    return
  }
 
  processCommand(data.command)
})
 
// ✅ Always specify target origin when sending
iframe.contentWindow.postMessage(
  JSON.stringify({ type: 'AUTH_SUCCESS', token }),
  'https://embedded.example.com' // Don't use '*' for sensitive data
)

Why It Matters

The Same-Origin Policy is the browser's primary defense against malicious sites stealing data or performing actions on behalf of your users. Misconfiguring CORS or failing to validate postMessage origins can allow attackers to bypass this protection — reading private data, submitting forms as users, or injecting content into your pages.

CORS Configuration

// ❌ Overly permissive — allows any site to make authenticated requests
app.use(cors({
  origin: '*',
  credentials: true  // This combination is invalid per spec, but dangerous if misconfigured
}))
 
// ✅ Explicit allowlist for authenticated endpoints
app.use(cors({
  origin: (origin, callback) => {
    const allowed = ['https://app.example.com', 'https://admin.example.com']
    if (!origin || allowed.includes(origin)) {
      callback(null, true)
    } else {
      callback(new Error('Not allowed by CORS'))
    }
  },
  credentials: true
}))
 
// ✅ Public APIs can use wildcard (without credentials)
app.use('/api/public', cors({ origin: '*' }))

fetch with Credentials

// Include cookies in cross-origin requests (requires CORS allow-credentials)
const response = await fetch('https://api.example.com/user', {
  credentials: 'include',  // Sends cookies
  headers: { 'Content-Type': 'application/json' }
})
 
// 'same-origin' (default) — only sends credentials on same-origin requests
// 'omit' — never sends credentials
// 'include' — always sends credentials (requires server CORS configuration)

CSRF Protection

// SameSite cookies prevent cross-origin form submissions from sending cookies
// Set on the server:
// Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
 
// For APIs that receive cross-origin requests, validate CSRF tokens:
async function submitForm(data) {
  const csrfToken = document.querySelector('meta[name="csrf-token"]').content
 
  await fetch('/api/submit', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': csrfToken
    },
    body: JSON.stringify(data)
  })
}

Prevent Open Redirects

Return-to flows often accept next, redirect, or callbackUrl parameters. Never navigate to them blindly:

// ❌ Bad: attacker controls the destination
const destination = new URLSearchParams(location.search).get('next')
window.location.assign(destination ?? '/dashboard')
 
// ✅ Good: allow-list internal destinations only
const nextParam = new URLSearchParams(location.search).get('next')
const safeDestination = nextParam?.startsWith('/') ? nextParam : '/dashboard'
window.location.assign(safeDestination)

Exceptions

  • A framework default or browser behavior is not an exception by itself; only documented constraints with compensating controls should suppress the finding.
  • When a JavaScript pattern looks unsafe but the data is fully constrained, validated, and never attacker-controlled, document that boundary explicitly instead of treating it as implicit.
  • If a rule overlaps with a stronger exploit path or runtime failure, fix the issue that most directly enables compromise or user-visible breakage first.

Standards

  • Use MDN: JavaScript Guide as the standard for how this JavaScript pattern should behave in production, not just in a small local example.
  • Use web.dev: Learn JavaScript as the standard for how this JavaScript pattern should behave in production, not just in a small local example.

Verification

  1. Verify the behavior in the browser after the code change, not only in static analysis.
  2. Inspect DevTools Network or Performance panels when the rule affects loading or execution order.
  3. Test the primary user flow and one edge case triggered by the changed script path.
  4. Confirm the code still behaves correctly when the feature is delayed, lazy-loaded, or fails.
  5. Try a malicious external redirect target such as ?next=https://attacker.example and confirm the app rejects or rewrites it.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Check this code for insecure cross-origin patterns: missing origin validation in postMessage listeners, overly permissive CORS settings, and missing CSRF protections. Flag redirect parameters that accept arbitrary external URLs.

Fix

Auto-fix issues

Add origin validation to postMessage listeners and review CORS configuration to ensure it's not overly permissive for authenticated routes. Reject or allow-list untrusted redirect destinations before navigating.

Explain

Learn more

Explain the Same-Origin Policy, how CORS works, and how to securely use postMessage for cross-origin communication.

Review

Code review

Review scripts, client components, and browser execution paths related to Handle cross-origin requests securely. Flag exact imports, event handlers, runtime side effects, or blocking operations that violate the rule, and state how the change should be verified in the browser. Check login, logout, SSO, and return-to flows for open redirects.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

MDN CORSdeveloper.mozilla.orgTool
MDN postMessagedeveloper.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Never use eval() or unsafe dynamic code execution

Avoid eval(), new Function(), setTimeout/setInterval with string arguments, and innerHTML with untrusted content — they execute arbitrary code and create critical XSS vulnerabilities.

JavaScript
Use Web Storage API safely

Use localStorage and sessionStorage with proper serialization, error handling, and security awareness to avoid data corruption and storage quota errors.

JavaScript
Implement proper error handling

Use try-catch blocks and error boundaries to gracefully handle errors in async operations and UI components.

JavaScript
Set Secure, HttpOnly, and SameSite flags on session cookies

All session and authentication cookies are issued with the Secure, HttpOnly, and an appropriate SameSite flag to prevent interception, JavaScript exfiltration, and cross-site request forgery.

Security

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385