Express.js / Intermediate: modules, identity and adapters / Lesson 10
Browser origins, CORS and CSRF boundaries
Separate browser response-read policy from authentication and protection against unwanted state changes.
On this page
Lesson goals
- Explain preflight, credentials and exact origin matching.
- Choose CSRF controls from how credentials reach the request.
CORS controls browser reading, not server authorization
CORS response headers tell a browser whether script from another origin may read a response. The middleware does not stop arbitrary clients from sending requests, and a non-browser caller does not obey a browser CORS policy. An origin includes scheme, host and port, which differs from the browser concept of a site used by SameSite cookies. Keep the two concepts precise. A permitted origin is not an authenticated principal, and a missing CORS header is not proof that a state-changing request never reached the server. Domain authorization remains necessary on every protected operation.
Allow the intended origin with an explicit credential policy
The illustrative middleware permits one synthetic portal origin and credentials. It does not install cors, configure cookie delivery or authenticate users. Credentialed browser reads cannot use a wildcard Access-Control-Allow-Origin response as a universal allow-all shortcut. A dynamic allowlist should return a matched approved origin and preserve correct cache variation when responses differ by Origin. Define treatment for missing and null origins according to the actual clients instead of silently approving them. Test preflight and actual requests separately, because a successful OPTIONS exchange does not prove a protected write is authorized.
app.use("/api", cors({
origin:"https://portal.example.invalid",
credentials:true,
methods:["GET","POST"],
allowedHeaders:["Content-Type","X-CSRF-Token"]
}));
The .invalid origin is synthetic. The fragment sets a browser policy through an assumed middleware and performs no request.
Assess unwanted writes from the credential mechanism
CSRF concerns a browser performing an unwanted authenticated action using authority it attaches automatically, often cookies. Use a reviewed token or equivalent protocol, origin checks where appropriate and suitable cookie attributes; no single universal setting covers every application. Keep GET free of intentional state-changing behavior. A server that applies authentication but accepts a forged action from another context can still be vulnerable even if the attacker cannot read the response. Conversely, token-in-header architectures have different browser attachment behavior and still require protection against token theft and script compromise.
Reject suffix tricks with exact parsed origins
The offline predicate compares a parsed URL origin with one fixed approved origin. It rejects a deceptive host that merely starts with the expected text and rejects malformed input. This is a small allowlist model, not a complete CORS implementation: browser serialization, null origins, credentials and preflight still need integration tests. Avoid a loose contains or endsWith rule that accidentally includes an attacker-controlled sibling domain. If several origins are approved, store exact normalized origins with a reviewed change process rather than expanding a substring rule until errors disappear.
function approvedOrigin(value) {
try {return new URL(value).origin === "https://portal.example.invalid";}
catch {return false;}
}
console.assert(approvedOrigin("https://portal.example.invalid"));
console.assert(!approvedOrigin("https://portal.example.invalid.attacker.invalid"));
console.assert(!approvedOrigin("not-a-url"));
The exact synthetic origin passes; the deceptive host and malformed value fail. URL parsing opens no connection.
Inspect each boundary without weakening unrelated controls
For a browser failure, inspect method, Origin, preflight response, actual response, credential mode, cookie eligibility and server authorization outcome. Do not solve a login issue by allowing every origin or disabling CSRF. A request can fail in the browser after the server already performed its action, so preserve operation identity when investigating retries. Test allowed and denied origins with safe fixtures in an authorized isolated environment; this course validates only the local comparison. Browser enforcement, cookie behavior and actual middleware header generation remain unexecuted integration boundaries.
Common errors and fixes
CORS is treated as authentication
- Why it happens
- Browser read permission was confused with caller authority.
- How to fix it
- Authenticate and authorize independently of origin headers.
- Prevent it next time
- Test a non-browser caller under the same domain policy.
A credentialed response uses a wildcard origin
- Why it happens
- The credential mode and response policy were incompatible.
- How to fix it
- Return an approved explicit origin with correct cache variation.
- Prevent it next time
- Test preflight and actual credentialed reads.
A forged write succeeds despite unreadable response
- Why it happens
- CORS was expected to prevent CSRF actions.
- How to fix it
- Apply the reviewed CSRF protocol for automatic credentials.
- Prevent it next time
- Test state effects as well as browser-readable responses.
Check your understanding
1. Why is a failed browser read not proof that a write did not happen?
Show worked solution
The server may receive and execute the request before the browser rejects response access. CORS governs that access, while domain execution and CSRF are separate concerns. Preserve an operation identifier and reconcile the authoritative outcome before retrying a write; do not infer rollback from the browser console message alone.
2. Does the URL predicate test actual CORS middleware?
Show worked solution
No. It checks exact origin comparison and rejection of two local fixtures. It does not exercise headers, credentials, preflight or a browser. Keep its narrow evidence, then separately verify the composed application and browser policy with synthetic requests when that integration is authorized.
Key takeaways
- CORS, CSRF and authentication protect different boundaries.
- Browser errors do not establish the absence of server effects.