Web Security / Intermediate: browser and data interpretation defenses / Lesson 9
CSRF and request authenticity
Protect state-changing browser requests under the actual credential model without confusing CORS, HttpOnly, or authentication with user intent.
On this page
Lesson goals
- Identify when browser-attached credentials need request-authenticity defenses.
- Review token, origin, SameSite, and workflow integration separately.
Separate a valid session from an intended action
A browser can attach credentials to certain requests under cookie and fetch rules even when the user did not intend the application action. CSRF defenses help a service distinguish authorized workflow requests from unwanted cross-site initiation under its session model. Authentication and resource permission are still required, but neither alone proves the request’s intended origin or workflow. Use appropriate methods for state changes and do not put sensitive mutations behind a safe navigation GET. Understand whether the actual client uses browser-attached cookies, explicit tokens, or another supported mechanism before copying a defense from a different architecture.
Use the framework’s reviewed token mechanism
A synchronizer token or an appropriately designed double-submit mechanism has specific generation, binding, transmission, and validation requirements. Prefer maintained framework middleware and its documented integration rather than inventing a token from a predictable identifier. Do not put private tokens in URLs or ordinary logs. The planning record below states the questions without generating a credential or configuring a route. Token presence is not enough if the application never validates it against trusted context. Reauthentication or explicit user confirmation may be appropriate for especially sensitive actions, but those controls must be integrated deliberately rather than used to excuse a missing baseline defense.
{
"operation": "update-private-note",
"credentialModel": "browser-session-cookie",
"tokenMechanism": "reviewed-framework-integration",
"tokenBinding": "validated-session-context",
"originPolicy": "explicit-allowed-workflow",
"denyEffect": "no-mutation",
"containsToken": false
}
This file documents requirements only. It contains no token and creates no browser request, session, or server policy. The actual framework’s token and proxy/host integration still require authorized testing.
Give browser signals their limited role
Origin or related request metadata can support a reviewed source policy, but missing, opaque, or transformed context needs explicit handling appropriate to the supported workflow. Trusted proxy configuration matters when reconstructing the target origin. SameSite cookies can reduce some cross-site credential sending, but site is not origin and compatibility and business flows matter. HttpOnly restricts cookie script access rather than establishing request authenticity. CORS controls a browser’s cross-origin response exposure and is not a universal CSRF defense. XSS can undermine many client-side assumptions, so maintain output safety and permission as complementary boundaries.
Test an explicitly synthetic comparison rule
The local function compares public lab byte markers using a standard comparison helper and an already-reviewed origin boolean. It is deliberately not a complete CSRF implementation. The markers are not secret and must never become production tokens. A real mechanism needs unpredictable values, correct binding, trusted source handling, expiry or lifecycle rules, and enforcement before side effects. This small test demonstrates refusal when the supplied marker or reviewed context disagrees. It performs no browser action and cannot validate cookie sending, SameSite, framework middleware, or a deployed proxy path.
import hmac
def csrf_fixture(expected, presented, origin_reviewed):
if not isinstance(expected, bytes) or not isinstance(presented, bytes):
return False
return origin_reviewed and hmac.compare_digest(expected, presented)
marker = b"public-synthetic-marker-not-a-token"
assert csrf_fixture(marker, marker, True)
assert not csrf_fixture(marker, b"other-public-marker", True)
assert not csrf_fixture(marker, marker, False)
All bytes are public synthetic data. Passing assertions show only the declared comparison outcomes. No token is issued, no session is authenticated, and no state-changing request is sent.
Verify legitimate forms and rejected mutations together
A correct defense must allow the intended authenticated workflow while rejecting unsupported requests without mutation. Verify token placement, parser and content-type behavior, session changes, expired forms, multiple tabs, and error recovery in an authorized isolated environment. Do not disable validation globally when a legitimate form fails; trace the exact mismatch and correct the integration. Keep failure responses useful without exposing token values. Record browser, framework, and configuration assumptions, and ensure alternate API or background paths receive the controls appropriate to their credential model. The course validates only benign local fixtures, not an operational attack or a live site.
Common errors and fixes
HttpOnly is expected to stop forged actions
- Why it happens
- Cookie reading and credential sending are confused.
- How to fix it
- Use the appropriate request-authenticity mechanism.
- Prevent it next time
- Review each browser control’s exact scope.
A valid form fails and protection is disabled
- Why it happens
- Integration diagnosis is replaced by a global bypass.
- How to fix it
- Trace token/session/origin handling and repair the narrow mismatch.
- Prevent it next time
- Keep allow and deny workflow tests.
Rejected requests still modify data
- Why it happens
- Validation occurs after the protected effect.
- How to fix it
- Enforce the decision before mutation.
- Prevent it next time
- Assert unchanged state for failure paths.
Check your understanding
1. A CORS policy hides a response from another origin. Does that prove no state-changing request reached the server?
Show worked solution
No. Browser response exposure and request execution are different questions. Apply CSRF controls for the actual credential and workflow model and verify the denied request has no protected effect.
2. Why is the public marker test not a production token system?
Show worked solution
It lacks secret generation, session binding, transport integration, trusted context, and lifecycle enforcement. It demonstrates one comparison policy only. Use maintained framework mechanisms and verify the complete route behavior separately.
Key takeaways
- A valid session does not prove an intended request.
- Tokens, origin, SameSite, CORS, and HttpOnly are not interchangeable.
- Refusal must happen before protected effects.