RoastMyOpsec

Safety

HttpOnly vs Secure vs SameSite Cookies for Session OPSEC

May 10, 2024

HttpOnly vs Secure vs SameSite is not an either-or decision for session cookies. HttpOnly keeps the cookie out of document.cookie. Secure stops it from traveling on HTTP. SameSite limits when the browser attaches it to cross-site requests. Auth and session cookies on a public site should usually carry all three: Secure, HttpOnly, and SameSite=Lax or Strict. RoastMyOpsec grades session-like cookies on the landing Set-Cookie headers only — no login automation — so absence can be inconclusive if the cookie is issued later.

The practical difference

These flags solve different theft paths. Missing HttpOnly means any XSS on the page can read the session. Missing Secure means a cleartext hop can leak it. Missing or None SameSite means the cookie may ride along on cross-site GET/POST depending on browser defaults and your Chrome-era exceptions.

Marketing pixels and preference cookies are not the same as session cookies. Grade the cookies that look like sessions, auth, or JWTs first. Analytics cookies can stay less privileged; they should never be your login.

How to choose based on the cookie's job

Start with what the cookie authorizes. If it can change account state or prove login, it is a vault key and gets the hard flags. If it only stores a theme or a cart id without auth, the blast radius is smaller but Secure is still cheap on an HTTPS site.

Cookie jobHttpOnlySecureSameSiteTakeaway
Session / authRequiredRequiredLax or StrictTreat as a credential. Never readable to JS.
CSRF token cookieOften false (JS must read it)RequiredStrict or Lax with server Origin checksDifferent pattern — do not copy session flags blindly
PreferencesOptionalRequired on HTTPS sitesLax is usually enoughDo not store secrets here
Third-party embedDepends on vendorRequiredNone requires Secure and a real cross-site needAvoid None unless the product actually embeds cross-site

When SameSite=Lax wins

Lax wins for most cookie-based web apps that need top-level GET navigations from email or search to land the user logged in. It still blocks many cross-site POSTs from attaching the cookie.

Strict wins when the app is a high-value console and you would rather make users click through your own origin than accept inbound cross-site GETs with credentials.

When SameSite=None is a trap

None is for genuine cross-site credentialed embeds. It requires Secure. Using None 'so OAuth works' without understanding the callback is a common way to reopen CSRF-shaped problems.

If you do not have a third-party iframe that must send your session cookie, you do not need None.

What a public scan can and cannot see

RoastMyOpsec parses Set-Cookie on the URL you submit. If the session cookie is only issued after login, the free cookie check may be inconclusive. That is honest grading, not a pass.

Fix flags in the app or at the edge, then hit a URL that actually sets the cookie — or confirm in your browser's Application panel on an account you own.

Common mistakes

The first mistake is putting session tokens in document.cookie so frontend JS can 'just read them.'

The second mistake is Secure on production and missing it on the staging host that still uses real accounts.

The third mistake is copying a marketing-site cookie policy onto the app domain that holds sessions.

Free audit the URL you own

RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.

Free audit now

FAQ

Do I need HttpOnly, Secure, and SameSite on every cookie?
You need all three on session and auth cookies. Other cookies should still be Secure on HTTPS sites. HttpOnly depends on whether JavaScript must read the value.
Is SameSite=Lax or Strict better?
Lax is the usual default for sites that receive logged-in traffic from external links. Strict is stronger for consoles where inbound cross-site GETs should not carry the session.
Why did an OPSEC scan not see my auth cookie?
Public scans only see Set-Cookie on the requested URL. Cookies issued after login are often invisible without authenticated testing. Check the response that actually establishes the session.

Sources

Related guides