Three attributes decide whether a cookie can be stolen, read by injected script,
or replayed by another site. SecScan checks all three on every cookie in
Set-Cookie.
Severity depends on what the cookie is
The same missing flag means very different things depending on what the cookie
holds, so the checks are scored separately. SecScan classifies cookies by name —
session, token, jwt, sid, PHPSESSID and similar patterns are treated as
session or auth cookies; everything else (UI-state flags, analytics IDs) is not.
| Missing flag | On a session cookie | On everything else |
|---|---|---|
Secure | High | Low |
HttpOnly | Medium | Info |
SameSite | Medium | Info |
A theme-preference cookie without HttpOnly is fine — the front end has to read
it. A session cookie without HttpOnly means any XSS on your domain is an
account takeover. Reporting both at the same severity would train you to ignore
the finding.
What each flag does
Secure — the cookie is only ever sent over HTTPS. Without it, a single
plain-HTTP request to your domain (a hardcoded http:// link, a captive portal,
a stale bookmark) puts the session token on the wire in cleartext.
HttpOnly — JavaScript cannot read the cookie via document.cookie. This
does not prevent XSS; it removes the easiest thing an XSS payload does with it.
SameSite — controls whether the cookie rides along on cross-site requests.
SameSite=Lax— sent on top-level navigations, not on cross-site subrequests. The right default for session cookies.SameSite=Strict— never sent cross-site. Safest, but a user following a link from an email arrives logged out.SameSite=None— always sent, and requiresSecure. Only for cookies that genuinely need to work in a third-party context, such as an embedded widget.
Browsers now treat a cookie with no SameSite attribute as Lax, but relying on
that default is fragile — the behaviour differs across browsers and versions, and
it leaves your intent unstated.
Setting them
Set-Cookie: session=…; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400
In Express:
res.cookie('session', token, {
secure: true,
httpOnly: true,
sameSite: 'lax',
path: '/',
maxAge: 86_400_000,
});
Secure breaks local development over plain HTTP. Gate it on environment
(secure: process.env.NODE_ENV === 'production') rather than turning it off
everywhere and forgetting.
Related
Cookie theft via injected script is the delivery mechanism a
Content-Security-Policy is meant to
contain. The two findings compound: no CSP plus no HttpOnly means one reflected
XSS is a full session compromise.
Last reviewed 2026-09-07