Cookies are the primary mechanism for maintaining session state in web applications. A stolen or manipulated session cookie gives an attacker full access to a user's account. Despite this critical role, cookies are consistently misconfigured in production applications. A 2023 survey found that over 40% of web applications had at least one cookie security misconfiguration.
The problem is not that cookie security is difficult -- it is that the secure defaults are not always the defaults. Developers must explicitly opt into secure cookie configuration, and a single missing attribute can expose the session to theft.
Essential Cookie Attributes
Secure flag. The Secure attribute ensures the cookie is only sent over HTTPS connections. Without it, the cookie is transmitted in cleartext over HTTP, where it can be intercepted by network attackers.
Set-Cookie: session=abc123; Secure
Every authentication-related cookie must have the Secure flag. There is no legitimate reason to omit it on a production application that uses HTTPS.
HttpOnly flag. The HttpOnly attribute prevents JavaScript from reading the cookie via document.cookie. This defends against XSS attacks that try to steal session tokens.
Set-Cookie: session=abc123; HttpOnly
Session cookies should always be HttpOnly. Only omit this for cookies that JavaScript legitimately needs to read (like CSRF tokens in some architectures).
SameSite attribute. SameSite controls whether the cookie is sent with cross-site requests, defending against CSRF attacks.
SameSite=Strict means the cookie is never sent with cross-site requests. This is the most secure but can break legitimate cross-site flows (like following a link from an email to your application -- the user would not be logged in).
SameSite=Lax means the cookie is sent with top-level navigations (clicking a link) but not with embedded requests (images, iframes, AJAX). This balances security with usability and is the recommended default.
SameSite=None means the cookie is sent with all requests, including cross-site. This is required for legitimate cross-site use cases (like embedded iframes) but requires the Secure flag.
Domain attribute. Omit the Domain attribute to restrict the cookie to the exact origin. Setting Domain=example.com makes the cookie available to all subdomains, including any that might be compromised.
Path attribute. Set the Path to the narrowest applicable path. A session cookie for /app should not be sent with requests to /public.
Expires/Max-Age. Set appropriate expiration times. Session cookies (no expiry) are deleted when the browser closes. Persistent cookies survive browser restarts and should have reasonable expiration times based on your security requirements.
Cookie Prefixes
Cookie prefixes are an underused security feature that prevents cookie manipulation:
__Secure- prefix. Cookies with names starting with __Secure- must be set with the Secure flag from an HTTPS origin. This prevents HTTP pages from overwriting secure cookies.
Set-Cookie: __Secure-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/
__Host- prefix. The stricter prefix. Cookies with __Host- must be set with Secure, must not specify a Domain, and must have Path=/. This prevents subdomain attacks and ensures the cookie is locked to the exact origin.
Set-Cookie: __Host-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/
Use __Host- for session cookies. It provides the strongest cookie isolation available.
Common Misconfigurations
Missing Secure flag. A session cookie without Secure can be stolen by a network attacker performing SSL stripping. Even if your application redirects HTTP to HTTPS, the initial HTTP request may include the cookie.
Missing HttpOnly flag. Without HttpOnly, any XSS vulnerability in your application can steal the session cookie. Given that XSS is the most common web vulnerability, this is a significant risk.
SameSite=None without justification. Setting SameSite=None for convenience (because Strict or Lax broke something) disables CSRF protection. Fix the compatibility issue instead of weakening security.
Overly broad Domain. Setting Domain=.example.com exposes the cookie to every subdomain. If any subdomain is compromised (a legacy application, a development server, a user-content domain), the session cookie is exposed.
Long-lived session cookies. A session cookie with a 30-day expiry that is stored on a shared computer compromises the account for 30 days. Balance convenience with risk based on your application's sensitivity.
Not rotating session IDs. The session ID should change after authentication (login), privilege elevation, and at regular intervals during long sessions. This limits the window of exposure for stolen session tokens.
Framework-Specific Guidance
Most web frameworks have configuration options for cookie security, but the defaults vary:
Express.js. Use the express-session middleware with cookie: { secure: true, httpOnly: true, sameSite: 'lax' }. The defaults are not fully secure.
Django. Set SESSION_COOKIE_SECURE = True, SESSION_COOKIE_HTTPONLY = True, and SESSION_COOKIE_SAMESITE = 'Lax' in settings.py. Django 2.1+ defaults SameSite to Lax.
Spring Boot. Configure in application.properties: server.servlet.session.cookie.secure=true, server.servlet.session.cookie.http-only=true, server.servlet.session.cookie.same-site=lax.
ASP.NET Core. Configure in Startup.cs using CookiePolicyOptions with MinimumSameSitePolicy, Secure, and HttpOnly settings.
Testing Cookie Security
Test your cookie configuration by inspecting Set-Cookie headers in browser developer tools, using the OWASP ZAP or Burp Suite cookie analyzer, running automated checks with tools like testssl.sh or Mozilla Observatory, and writing integration tests that verify cookie attributes on authentication responses.
How Safeguard.sh Helps
Safeguard.sh monitors your web application's security configuration, including cookie settings, as part of its comprehensive security assessment. The platform identifies insecure cookie configurations and tracks remediation across your application portfolio. Combined with dependency monitoring and SBOM generation, Safeguard.sh ensures that your application's session security is not undermined by either misconfigured cookies or compromised dependencies.