A session identifier is an opaque reference to server-side state. After authentication, a server can issue it in a cookie marked HttpOnly and Secure, with a SameSite policy suited to the flow. HttpOnly limits script access to the cookie; it does not stop the browser from sending the cookie with an eligible request. That automatic sending is why a state-changing endpoint needs a cross-site request forgery defense. A server-issued token bound to the active session can be checked on writes, alongside appropriate origin checks. SameSite is useful defense in depth, not a complete replacement for the write check. The model below generates unpredictable session and token values and compares the submitted token only after finding the session.
Sessions and CSRF: keep identity on the server
Case study
A reviewer is signed in and opens an inspection form. The server associates a fresh anti-forgery token with the session and includes it in that form. A write carrying the session cookie but no valid token is rejected before any record changes. Signing out invalidates the server-side session; merely deleting a browser display flag would not. In production, cookie scope, expiration, rotation, transport security, and login rate limits belong to the authentication design. This code demonstrates one boundary rather than a full login service.
Working contract
const { randomBytes, timingSafeEqual } = require("node:crypto");
const sessions = new Map();
const sessionId = randomBytes(24).toString("hex");
const writeToken = randomBytes(24).toString("hex");
sessions.set(sessionId, { reviewerId: 47, writeToken });
function mayWrite(cookieSessionId, submittedToken) {
const session = sessions.get(cookieSessionId);
if (!session || typeof submittedToken !== "string") return false;
const expected = Buffer.from(session.writeToken, "hex");
const supplied = Buffer.from(submittedToken, "hex");
return expected.length === supplied.length && timingSafeEqual(expected, supplied);
}
console.log(mayWrite(sessionId, writeToken));
console.log(mayWrite(sessionId, "invalid"));Observed output
true
falseCost and tradeoffs
Map lookup is expected O(1), while comparing fixed-size token buffers is O(K) for token byte length K. Keeping active sessions consumes O(S) memory or equivalent durable storage for S sessions. Random generation has a small fixed cost per session. A distributed service needs shared session state or a deliberate stateless design; copying a session map to every process would make sign-out inconsistent. Never log the raw cookie or token to troubleshoot failures. A forged request can arrive even when the user has not opened your form, so the server must own this check.
Common Mistakes
- Do not keep bearer session identifiers in localStorage for convenience.
- Do not treat SameSite alone as a complete write defense.
- Do not compare a token before verifying its session exists.
- Do not confuse a CSRF token with authorization to edit a particular record.
Continue through the stack
Form submission: validate on the server and return field errors; Browser storage: save convenience data without storing credentials; URLs and origins: separate location, query, and fragment.
Failure trace
A signed-in browser automatically attaches its session cookie to a forged write request initiated from another page. The server checks only that the cookie is valid and records the attacker's chosen note. Bind the mutation to a CSRF defense suited to the session design, check the request's intended origin where appropriate, and keep the session reference out of script-readable storage when possible.
Verification
- Submit an authenticated write without the expected CSRF proof and confirm rejection.
- Try the same request with a valid proof and verify the stored result and response status.
- Sign out, replay the old cookie, and confirm the server no longer recognizes that session.
Decision note
Cookie flags narrow exposure but do not replace a complete write defense. Session validation, request intent, and record-level permission are separate checks that all matter before storage changes.
Advanced connections
Password Verification and Login Budgets; Account Recovery and Session Revocation; Federated Login Callback Boundary.
