Handle cross-origin requests securely
Use CORS correctly, validate message origins with postMessage, and understand the Same-Origin Policy to prevent cross-origin attacks.
- 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`
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
- Verify the behavior in the browser after the code change, not only in static analysis.
- Inspect DevTools Network or Performance panels when the rule affects loading or execution order.
- Test the primary user flow and one edge case triggered by the changed script path.
- Confirm the code still behaves correctly when the feature is delayed, lazy-loaded, or fails.
- Try a malicious external redirect target such as
?next=https://attacker.exampleand 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.