JavaScripthighsecurity
Handle cross-origin requests securely
rule · cross-origin-security
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
JavaScript
// ❌ 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
JavaScript
// ❌ 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
JavaScript
// 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
JavaScript
// 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:
TypeScript
// ❌ 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.