Course outline

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.

About 4 min readContent reviewed
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.

json A synthetic request-authenticity design record
{
  "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.

python A complete offline marker-comparison fixture
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.

Official references