Why harden cookies?
Cookies — especially session cookies — are the authentication mechanism of the web. Whoever steals the cookie is logged in. Three classic attacks target cookies: XSS (a script steals the cookie via document.cookie), man-in-the-middle (the cookie is read in transit on an unencrypted connection) and CSRF (a third-party site uses the cookie for unauthorized actions). For each attack there is a matching cookie flag — and all three should be set.
Secure
With Secure the browser only
sends the cookie over HTTPS. On an HTTP connection the cookie is neither set nor sent
back. As a result, an attacker on the same Wi-Fi cannot intercept cookies from an
unencrypted connection. Mandatory for every auth cookie.
HttpOnly
With HttpOnly the cookie becomes
invisible to JavaScript — accessing it via
document.cookie returns nothing.
That makes an XSS vulnerability significantly less dangerous: even if foreign code is
running in the user's browser, it cannot steal the session cookie. Mandatory
for every auth cookie.
SameSite
SameSite controls whether the
cookie is sent on cross-site requests. Three values:
SameSite=Strict— the cookie is never attached to cross-site requests. Maximum security, but UX problems: a user clicking from Google to your site is not logged in on the first visit.SameSite=Lax(default since 2020) — the cookie is sent on top-level navigation (clicking a link), but not on iframes, images or POST requests from foreign sites. A good compromise.SameSite=None— the cookie is always sent. RequiresSecureas well. Only needed for third-party cookies (tracking, embedded widgets).
__Host- and __Secure- prefixes
Special cookie name prefixes enforce certain flags. Cookies starting with
__Host- must have
Secure, no
Domain attribute and
Path=/.
__Secure- only enforces
Secure. Both prefixes
additionally protect against cookie-tossing attacks from subdomains.
Configuration examples
Flask:
app.config['SESSION_COOKIE_SECURE'] = True app.config['SESSION_COOKIE_HTTPONLY'] = True app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
Express (Node.js):
app.use(session({
cookie: { secure: true, httpOnly: true, sameSite: 'lax' }
}))
PHP (session.cookie_secure etc. via php.ini or ini_set):
session.cookie_secure = 1 session.cookie_httponly = 1 session.cookie_samesite = "Lax"
What cookie flags do not protect against
- Logical CSRF despite SameSite=Lax — on GET requests that change state (which they shouldn't anyway). Solution: GET = read-only, otherwise use a CSRF token.
- Session hijacking via XSS — if the attacker can run code directly in your context, they can perform actions instead of stealing cookies. CSP against XSS remains important.
- Cookie content — flags protect transport, not content. Sensitive data does not belong in the cookie; keep it server-side in the session store.
Recommendation
For every auth cookie:
Secure; HttpOnly; SameSite=Lax.
For strict security (banking, admin panels):
SameSite=Strict. An additional
__Host- prefix costs nothing
and rules out subdomain attacks.