CWE-346: Origin Validation Error
Overview
Origin validation errors occur when an application does not verify where a request came from before acting on it. The usual forms are accepting cross-origin requests without a check, trusting the Referer header, and misconfigured CORS. The result is CSRF, cross-site data theft, and unauthorized cross-domain access.
Relationship to Other CWEs
- CWE-346 (this page) - the general category of acting on a request without verifying where it came from, covering CORS misconfiguration, Referer trust, and CSRF.
- CWE-352 (Cross-Site Request Forgery) - the most common specific form: forged state-changing requests riding on a victim's session. It has deeper, language-specific remediation guidance covering the synchronizer token pattern and framework CSRF middleware. If the issue is CSRF itself, use the CWE-352 guidance; use this page for broader CORS and Referer-trust issues.
- CWE-942 (Permissive Cross-domain Security Policy with Untrusted Domains) - covers CORS in depth. For a wildcard, reflected, or over-broad
Access-Control-Allow-Origin, or acrossdomain.xml,clientaccesspolicy.xml, orpostMessagehandler that trusts any sender, go there for the full treatment: per-endpoint policies, the pitfalls around suffix matching and thenullorigin, and language-specific configuration.
OWASP Classification
A07:2025 - Authentication Failures
Risk
High: Without an origin check, a page the attacker controls can drive state-changing requests that carry the victim's session cookie (CSRF), read responses a permissive CORS policy exposes to it, and open an authenticated WebSocket connection on the victim's behalf.
Remediation Steps
Core Principle: Validate the origin of security-relevant data; bind it to a trusted identity/channel before acting on it.
Locate the origin validation error
- Look for endpoints that accept cross-origin requests without validation: CORS misconfiguration, missing CSRF tokens, no Origin or Referer check
- Find state-changing operations (POST, PUT, DELETE) that do not verify the request origin: form submissions, API calls, WebSocket connections
- Check the CORS configuration for
Access-Control-Allow-Origin: *, Origin headers reflected without validation, andAllow-Credentials: truecombined with a wildcard - Follow a cross-origin request from receipt, through the origin check or the place one is missing, to the action it triggers
- Work out what an attacker gains: CSRF by submitting forms as the victim, data exfiltration through cross-origin API calls, or WebSocket hijacking
Implement proper CORS configuration (Primary Defense)
- Match the Origin header server-side: echo
Access-Control-Allow-Originonly when the incoming origin is an exact entry in a fixed list. Not a wildcard*, not the reflected header, and not a suffix or substring match. Example:Access-Control-Allow-Origin: https://trusted-app.example.com - Never allowlist the literal
nullorigin: an attacker can make a request carryOrigin: nullfrom a page they fully control - a sandboxed iframe, adata:URL, or certain redirect chains - sonullin the list is an allowlist that admits anyone - Send
Vary: Originwith the response: without it, a shared cache can hand the response built for one allowed origin to a different origin entirely - Set
Access-Control-Allow-Credentials: trueonly alongside a specific validated origin, never with* - For per-endpoint policies, the middleware-ordering pitfall, and framework configuration, follow CWE-942 rather than repeating the allowlist logic here
Use anti-CSRF tokens for state-changing operations
- Synchronizer token pattern: generate a unique token per session, include it in forms and AJAX requests, and validate it on the server
- Signed double-submit cookie pattern: where there is no server-side session store to hold the token, HMAC it with a server-side secret and bind it to the session before comparing the cookie against the request value. Comparing a raw cookie to a raw parameter is bypassable by anyone who can write cookies on the domain - a related subdomain, or a cookie-setting bug elsewhere on the site - because they can set both halves themselves
- SameSite cookie attribute: set
SameSite=StrictorSameSite=Laxon session cookies - Validate tokens on state-changing operations: POST, PUT, DELETE and PATCH requests must carry a valid CSRF token
- Framework support: use the framework's own CSRF protection or a maintained middleware - Django, Rails, Spring Security, maintained Express or Fastify middleware. Confirm the package is still maintained before adopting it; the long-standing Express
csurfpackage is archived
Validate request origin (Defense in Depth)
- Check the Origin header: modern browsers send it on cross-origin requests
- Fall back to Referer only when Origin is missing: Referer can be suppressed by browser settings or proxies, so treat it as a secondary check, never the only one
- Fail closed when both headers are absent: sensitive operations should require a valid Origin or Referer
- Combine with CSRF tokens: origin validation alone is not sufficient
Use SameSite cookie attribute
- SameSite=Strict (strongest browser-enforced isolation): cookie sent only for same-site requests. That blocks cross-site requests, which is not the same as blocking all CSRF: a sibling application on the same registrable domain is same-site, XSS on any subdomain can issue the request from inside the boundary, and a cookie injected over plaintext HTTP is still accepted by the HTTPS site
- SameSite=Lax (less disruptive): cookie sent for top-level navigation only, which still blocks CSRF on POST and API requests
SameSite=NonerequiresSecure: browsers reject aNonecookie that is not also markedSecure, so the cookie is never set- Apply to session cookies: every authentication and session cookie should carry SameSite
- Combine with CSRF tokens: SameSite is defense in depth, not a replacement for a server-side token
Check the Origin header on WebSocket handshakes
Browsers do not apply the same-origin policy to the WebSocket handshake and send no preflight for it, so new WebSocket(...) from any page reaches your server with the victim's cookies attached. Nothing on the browser side can be configured to stop that: the server has to read the Origin header on the handshake request itself and reject anything outside its allowlist. Where the connection carries authority, require an application token in the first message as well, rather than relying on the session cookie alone.
Test origin validation
- Send cross-origin requests from an untrusted domain; they should be rejected
- Submit a form from an attacker site to the victim site; it should fail without a token
- Verify the CORS headers only allow trusted origins
- Send requests with no Origin or Referer header; sensitive operations should reject them
- Verify SameSite cookies are not sent on cross-site requests
- Open a WebSocket connection from an untrusted origin and confirm the server rejects the handshake
- Send state-changing requests without a CSRF token; they should fail
- Re-scan to confirm the finding is resolved
Common Vulnerable Patterns
- Access-Control-Allow-Origin: *
- Reflecting Origin header without validation
- No CSRF token validation
- Trusting Referer header alone
- Allowlisting the literal
nullorigin - Accepting a WebSocket handshake without checking the Origin header
- Missing SameSite on session cookies