Cookie Security: SameSite, HttpOnly, Secure, and __Host- Prefix

A practical guide to session cookie security attributes. What each flag does, how to test them, and what happens when they're missing.

web-securitycookiestutorialtools

Session cookies are the keys to the kingdom. A stolen session cookie = account takeover, regardless of how strong the user’s password is. Browser security attributes are your first line of defense against XSS-based theft and CSRF attacks.

The Four Attributes

Secure

The cookie is only sent over HTTPS connections. Without this, the cookie can be intercepted on any HTTP request (coffee shop Wi-Fi, HTTP redirects, etc.).

Set-Cookie: session=abc123; Secure

HttpOnly

JavaScript cannot read the cookie via document.cookie. This is the primary defense against XSS-based session theft. It doesn’t prevent CSRF.

Set-Cookie: session=abc123; Secure; HttpOnly

SameSite

Controls when the browser sends the cookie in cross-site requests:

ValueBehavior
StrictNever sent in cross-site requests
LaxSent on top-level navigations (GET only)
NoneAlways sent — requires Secure
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax

SameSite=Lax is the browser default in modern browsers and a good baseline. Use Strict for high-security sessions (banking, admin panels). Only use None if you have a legitimate cross-site use case (OAuth, embedded widgets).

__Host- Prefix

The __Host- cookie name prefix enforces three things automatically:

  • Secure is set
  • No Domain attribute (can’t be shared with subdomains)
  • Path must be /
Set-Cookie: __Host-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/

This protects against subdomain-based cookie injection attacks.

Testing Cookies

Browser DevTools

  1. Open DevTools → Application → Cookies
  2. Check each column: Secure, HttpOnly, SameSite
  3. Look for ⚠️ warning icons (Chrome flags missing Secure on SameSite=None)

Command Line

# Check cookie attributes on any site
curl -sI https://example.com/login \
  -c /dev/null \
  -d "user=test&pass=test" \
  | grep -i set-cookie

# Expected output:
# set-cookie: session=abc; Path=/; Secure; HttpOnly; SameSite=Lax

Automated Testing with Python

import requests

def audit_cookies(url: str) -> None:
    r = requests.get(url, allow_redirects=True)
    for cookie in r.cookies:
        issues = []
        if not cookie.secure:         issues.append("MISSING Secure")
        if not cookie.has_nonstandard_attr("httponly"):
                                       issues.append("MISSING HttpOnly")
        samesite = cookie._rest.get("SameSite", "").lower()
        if samesite not in ("lax", "strict"):
                                       issues.append(f"SameSite={samesite or 'MISSING'}")

        status = "✅" if not issues else "❌"
        print(f"{status} {cookie.name}: {', '.join(issues) or 'OK'}")

audit_cookies("https://example.com")

What Burp Suite Shows You

In Burp’s Proxy → HTTP history, look at any response with Set-Cookie. The Scanner will flag missing attributes automatically in its passive checks. You can also use the “Cookie Issues” filter in the Scanner dashboard.

💡 Tips

  • Rotate session IDs after login even if cookies look fine — fixation attacks reuse pre-login cookies.
  • Short expiry on sensitive session cookies (Max-Age=3600) limits the window for cookie theft.
  • For SPAs using JWT in localStorage — this bypasses all cookie security. Prefer cookies with the attributes above over localStorage for auth tokens.

References