Insecure Cookie Settings: Missing httpOnly, Secure, and SameSite Flags - VibeDoctor 
← All Articles 🔒 Security Vulnerabilities High

Insecure Cookie Settings: Missing httpOnly, Secure, and SameSite Flags

AI-generated cookie code often skips httpOnly, Secure, and SameSite flags. Learn what each flag does and why your session cookies need them.

SEC-007

Quick Answer

When cookies lack the httpOnly, Secure, and SameSite flags, session tokens become accessible to JavaScript, transmittable over HTTP, and vulnerable to cross-site request forgery. AI-generated authentication code almost universally omits these flags. Adding all three takes under 10 lines of code and closes three distinct attack vectors at once.

Why Cookie Flags Are a Bigger Deal Than They Look

Cookies are how your app knows who's logged in. A session token stored in a cookie is essentially a key to a user's account - and the three security flags on that cookie determine whether attackers can steal or misuse it. Skip one flag and you open a specific attack vector. Skip all three and your authentication is seriously exposed.

According to OWASP, broken authentication - which includes insecure session management - remains in the OWASP Top 10 as one of the most exploited vulnerability categories. Veracode's 2023 State of Software Security report found that session management flaws appear in over 27% of scanned applications. For vibe-coded apps built with Bolt, Lovable, Cursor, or v0, this number is higher - AI tools optimize for "it works" over "it's secure."

GitGuardian's 2024 research noted that misconfigured authentication patterns, including cookie settings, are among the most common issues surfaced during security reviews of AI-assisted codebases. The good news: the fix is always the same and always fast.

The Three Cookie Flags and What Each One Does

Flag What it prevents Attack without it
httpOnly JavaScript cannot read the cookie XSS steals session token via document.cookie
Secure Cookie only sent over HTTPS Man-in-the-middle intercepts token over HTTP
SameSite=Strict Cookie not sent on cross-site requests CSRF forces authenticated actions from another site
SameSite=Lax Cookie sent on top-level navigations only Partial CSRF protection - safe for most apps
SameSite=None No CSRF protection Required for legitimate cross-site embeds (use with Secure)

Missing httpOnly is the most critical of the three. If your app has even one XSS vulnerability - and 65% of web applications do according to Veracode - an attacker can run document.cookie in the browser console and read your session token directly. They don't need your password; they just need to steal the cookie.

What AI-Generated Cookie Code Looks Like

When you ask Bolt, Lovable, or Cursor to "add user authentication with session cookies," here's the kind of code you often get back:

// ❌ BAD - No security flags on session cookie
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await verifyUser(email, password);

  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Session token stored without any security flags
  res.cookie('sessionToken', user.sessionId, {
    maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
  });

  res.json({ success: true });
});

This code works perfectly in development. The login functions. The cookie persists. But the cookie can be read by any JavaScript on the page (httpOnly missing), sent over plain HTTP connections (Secure missing), and included in cross-site requests (SameSite missing). Three attack surfaces, zero effort from the AI to close them.

The same issue appears in Next.js apps using cookies() from next/headers, in Express apps using cookie-session or express-session without proper options, and in Supabase auth flows where developers manually handle the session token.

How to Fix Cookie Settings Correctly

Here's the same login endpoint with all three flags set properly:

// ✅ GOOD - Secure cookie with all three flags
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await verifyUser(email, password);

  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  res.cookie('sessionToken', user.sessionId, {
    httpOnly: true,                      // JS cannot read this cookie
    secure: process.env.NODE_ENV === 'production', // HTTPS only in prod
    sameSite: 'lax',                     // Protects against most CSRF
    maxAge: 7 * 24 * 60 * 60 * 1000,   // 7 days in ms
    path: '/'
  });

  res.json({ success: true });
});

// ✅ GOOD - Next.js App Router with cookies()
import { cookies } from 'next/headers';

export async function POST(request: Request) {
  const { email, password } = await request.json();
  const user = await verifyUser(email, password);

  if (!user) {
    return Response.json({ error: 'Invalid credentials' }, { status: 401 });
  }

  cookies().set('sessionToken', user.sessionId, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 7,
    path: '/'
  });

  return Response.json({ success: true });
}

Note the secure: process.env.NODE_ENV === 'production' pattern. This lets you test locally over HTTP while enforcing HTTPS-only in production. On Vercel deployments, NODE_ENV is automatically set to production, so this just works.

SameSite Values: Which One Should You Use?

Choosing the right SameSite value depends on how your app is used:

SameSite=Strict - The most secure option. The cookie is never sent on any cross-site request, including when a user clicks a link from another site to your app. Use this for highly sensitive admin areas or banking-style apps where the extra friction is acceptable.

SameSite=Lax - The recommended default for most apps. The cookie is sent when users navigate directly to your site (top-level GET requests) but not on cross-site POST requests, iframes, or background fetches. This covers the most common CSRF attacks without breaking normal navigation.

SameSite=None - Required only when your cookie needs to be sent in cross-site contexts, such as embedded iframes or third-party widgets. Always combine this with Secure: true - browsers reject SameSite=None without HTTPS.

For a Next.js app deployed on Vercel with Supabase auth, SameSite=Lax is almost always the right answer. Supabase's own @supabase/ssr package sets this correctly by default - but AI tools sometimes bypass the package and write custom cookie logic that omits it.

Cookie Security in Popular Frameworks

Each major framework has its own cookie API, and AI tools handle them all differently:

Express / Node.js: Use the res.cookie() options object. If you're using express-session, set the cookie option in the session config: cookie: { httpOnly: true, secure: true, sameSite: 'lax' }.

Next.js App Router: Use cookies().set() from next/headers in Server Actions and Route Handlers. The options object accepts the same flags.

Next.js Pages Router: Use res.setHeader('Set-Cookie', serialize('token', value, { httpOnly: true, secure: true, sameSite: 'lax' })) from the cookie package.

Supabase auth: If you use @supabase/ssr correctly, cookie flags are handled. But if AI code manually calls supabase.auth.setSession() and stores tokens in cookies, you must set the flags yourself.

How to Find Insecure Cookies in Your Codebase

Start by searching your codebase for cookie-setting patterns: res.cookie(, cookies().set(, Set-Cookie, and cookie-session. For every hit, check whether the options object includes httpOnly: true, secure: true, and a sameSite value.

Tools like VibeDoctor (vibedoctor.io) automatically scan your codebase for insecure cookie configurations and flag specific file paths and line numbers. Free to sign up.

You can also verify cookie flags in the browser. Open DevTools, go to Application > Cookies, and look at the flags column. Any cookie without the HttpOnly checkbox ticked, or without the Secure flag shown, is vulnerable. This is especially useful for verifying your production Vercel deployment.

FAQ

Does httpOnly mean the user can't see their own cookie?

Yes - the cookie is hidden from JavaScript, including your own code. The browser still sends it automatically with every matching request. This is the intended behavior. You don't need JavaScript access to a session cookie; the browser handles it for you.

Will setting Secure break my localhost development?

Use secure: process.env.NODE_ENV === 'production' to conditionally apply the flag. In development, NODE_ENV is typically development, so the cookie works over HTTP. In production on Vercel or similar platforms, it's automatically production.

Is SameSite=Lax good enough to prevent CSRF?

For most apps, yes. Lax prevents cross-site POST requests from sending your cookie, which is the most common CSRF vector. If your app allows state-changing GET requests (which it shouldn't), you need Strict. Combine SameSite with CSRF tokens for defense in depth.

Does Supabase handle cookie security automatically?

Supabase's @supabase/ssr package sets secure cookie defaults when used correctly. The problem arises when AI tools generate custom authentication flows that bypass the package, or when developers handle the session token manually without applying the flags.

What's the risk if I don't fix insecure cookie settings?

Missing httpOnly combined with any XSS vulnerability lets attackers steal session tokens and take over accounts. Missing Secure exposes tokens on any HTTP connection (coffee shop Wi-Fi, shared networks). Missing SameSite enables CSRF attacks that perform actions as your users without their knowledge.

Scan your codebase for this issue - free

VibeDoctor checks for SEC-007 and 128 other issues across 15 diagnostic areas.

SCAN MY APP →
← Back to all articles View all 129+ checks →