Insecure Cookies | Blue Frog Docs

Insecure Cookies

Diagnose and fix cookies missing Secure, HttpOnly, and SameSite flags that expose users to session hijacking and CSRF attacks

Insecure Cookies

What This Means

Insecure cookies are cookies that lack proper security attributes (Secure, HttpOnly, SameSite), making them vulnerable to interception, theft, and misuse. These missing flags expose your users to serious security risks including session hijacking, cross-site scripting (XSS) attacks, and cross-site request forgery (CSRF).

Secure Flag:

  • Cookie only sent over HTTPS connections
  • Prevents interception over HTTP
  • Blocks man-in-the-middle attacks
  • Required for sensitive data

HttpOnly Flag:

  • Cookie not accessible via JavaScript
  • Prevents XSS attacks from stealing cookies
  • Blocks document.cookie access
  • Protects session tokens

SameSite Attribute:

  • Controls cross-site cookie sending
  • Prevents CSRF attacks
  • Three values: Strict, Lax, None
  • Required for modern browsers

Impact on Your Business

Security Vulnerabilities:

  • Session hijacking - Attackers steal user sessions, impersonate users
  • Account takeover - Stolen authentication cookies = full account access
  • Data theft - XSS scripts read sensitive cookie data
  • CSRF attacks - Malicious sites perform actions as logged-in users
  • Man-in-the-middle - Cookies transmitted over HTTP intercepted

Business Impact:

  • Users' accounts compromised
  • Financial transactions hijacked
  • Regulatory violations (GDPR, PCI DSS)
  • Legal liability for data breaches
  • Reputation damage and lost trust
  • Compliance audit failures

Common Attack Scenarios:

  1. Public WiFi attacker intercepts HTTP cookie
  2. XSS script steals session cookie via document.cookie
  3. Malicious site triggers CSRF using user's cookies
  4. Mixed content allows HTTP cookie transmission
  5. Subdomain takeover reads parent cookies

How to Diagnose

Method 1: Browser DevTools (Primary)

  1. Open your website
  2. Open DevTools (F12)
  3. Navigate to Application tab (Chrome) or Storage tab (Firefox)
  4. Expand Cookies in left sidebar
  5. Click your domain
  6. Review cookie list

What to Look For:

Cookie Name Secure HttpOnly SameSite Risk Level
session_id ❌ No ❌ No ❌ None πŸ”΄ Critical
auth_token βœ… Yes ❌ No ⚠️ None 🟑 High
user_prefs ❌ No βœ… Yes βœ… Lax 🟑 Medium
analytics βœ… Yes βœ… Yes βœ… Lax βœ… Secure

Red Flags:

  • Session/auth cookies without Secure
  • Session/auth cookies without HttpOnly
  • Any cookie with SameSite=None without Secure
  • Cookies over HTTP (not HTTPS)

Method 2: Network Tab Inspection

  1. Open DevTools β†’ Network tab
  2. Reload the page
  3. Click on the main document request
  4. Navigate to Headers section
  5. Scroll to Response Headers
  6. Find Set-Cookie headers

What to Look For:

❌ BAD:
Set-Cookie: session=abc123; Path=/; Domain=.example.com

⚠️ BETTER:
Set-Cookie: session=abc123; Secure; Path=/; Domain=.example.com

βœ… GOOD:
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/; Domain=.example.com

βœ… BEST:
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/; Domain=.example.com; Max-Age=3600

Method 3: Security Headers Checker

  1. Visit SecurityHeaders.com
  2. Enter your domain
  3. Review cookie warnings
  4. Check recommendations

What to Look For:

  • Cookie security warnings
  • Missing security attributes
  • Recommendations for improvement
  • Grade impact from insecure cookies

Method 4: Browser Console Check

Run this JavaScript in browser console:

// Get all cookies and analyze security
const cookies = document.cookie.split(';').map(c => c.trim());
console.log('Accessible cookies (potential XSS risk):', cookies);

// Note: HttpOnly cookies won't appear here (which is good for security)
// Secure and SameSite attributes not visible via document.cookie

// Check if site is HTTPS
console.log('Site using HTTPS:', window.location.protocol === 'https:');

// Warn about accessible cookies on HTTPS
if (cookies.length > 0 && cookies[0] !== '') {
  console.warn('⚠️ These cookies are accessible via JavaScript (missing HttpOnly):');
  cookies.forEach(c => console.warn('  -', c.split('=')[0]));
}

Method 5: OWASP ZAP or Burp Suite

Automated Security Testing:

  1. Install OWASP ZAP
  2. Configure browser to use ZAP proxy
  3. Browse your website
  4. Review Alerts tab
  5. Check for cookie security warnings

What to Look For:

  • "Cookie No HttpOnly Flag"
  • "Cookie Without Secure Flag"
  • "Cookie Without SameSite Attribute"
  • Severity ratings (High, Medium, Low)

Method 6: curl Command

# Check Set-Cookie headers
curl -I https://example.com

# Look for Set-Cookie headers
# Example output:
Set-Cookie: session=abc123; Path=/
Set-Cookie: user=john; Secure; HttpOnly; SameSite=Lax

# Analyze each cookie for missing flags

General Fixes

Best practice configuration:

Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/; Max-Age=3600

Breakdown:

  • Secure - Only sent over HTTPS
  • HttpOnly - Not accessible via JavaScript
  • SameSite=Strict - Never sent with cross-site requests
  • Path=/ - Available for entire domain
  • Max-Age=3600 - Expires in 1 hour

Node.js/Express:

const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser');

const app = express();

// Configure session with secure cookies
app.use(session({
  secret: 'your-secret-key',
  name: 'sessionId',
  cookie: {
    secure: true,           // Require HTTPS
    httpOnly: true,         // Prevent JS access
    sameSite: 'strict',     // CSRF protection
    maxAge: 3600000,        // 1 hour in milliseconds
    domain: '.example.com'  // Available to subdomains
  },
  resave: false,
  saveUninitialized: false
}));

// Set individual cookies
app.get('/login', (req, res) => {
  res.cookie('authToken', 'token123', {
    secure: true,
    httpOnly: true,
    sameSite: 'strict',
    maxAge: 86400000  // 24 hours
  });
  res.send('Logged in');
});

PHP:

<?php
// Set cookie with all security flags
setcookie(
    'session',              // name
    'abc123',              // value
    [
        'expires' => time() + 3600,
        'path' => '/',
        'domain' => '.example.com',
        'secure' => true,      // HTTPS only
        'httponly' => true,    // No JS access
        'samesite' => 'Strict' // CSRF protection
    ]
);

// Session cookie configuration
ini_set('session.cookie_secure', '1');
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_samesite', 'Strict');
session_start();
?>

Python/Flask:

from flask import Flask, session, make_response

app = Flask(__name__)
app.config.update(
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE='Strict',
)

@app.route('/login')
def login():
    response = make_response('Logged in')
    response.set_cookie(
        'authToken',
        'token123',
        secure=True,
        httponly=True,
        samesite='Strict',
        max_age=3600
    )
    return response

Python/Django (settings.py):

# Session cookie settings
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'
SESSION_COOKIE_AGE = 3600  # 1 hour

# CSRF cookie settings
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Strict'

Ruby/Rails:

# config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store,
  key: '_myapp_session',
  secure: true,
  httponly: true,
  same_site: :strict,
  expire_after: 1.hour

Java/Spring Boot:

// application.properties
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.same-site=strict
server.servlet.session.timeout=1h

// Or in Java config
@Configuration
public class CookieConfig {
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setUseHttpOnlyCookie(true);
        serializer.setUseSecureCookie(true);
        serializer.setSameSite("Strict");
        return serializer;
    }
}

ASP.NET Core:

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddSession(options =>
    {
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.HttpOnly = true;
        options.Cookie.SameSite = SameSiteMode.Strict;
        options.IdleTimeout = TimeSpan.FromHours(1);
    });

    // For authentication cookies
    services.ConfigureApplicationCookie(options =>
    {
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.HttpOnly = true;
        options.Cookie.SameSite = SameSiteMode.Strict;
    });
}

Fix 3: Web Server Configuration

Nginx (header modification):

# In nginx.conf or site config
location / {
    # Modify Set-Cookie headers to add security flags
    proxy_cookie_path / "/; Secure; HttpOnly; SameSite=Strict";

    # For existing cookies, use headers-more module
    # more_set_headers 'Set-Cookie: $sent_http_set_cookie; Secure; HttpOnly; SameSite=Strict';
}

Apache (.htaccess):

# Modify Set-Cookie headers
Header edit Set-Cookie ^(.*)$ "$1; Secure; HttpOnly; SameSite=Strict"

# Or with mod_headers
<IfModule mod_headers.c>
    Header always edit Set-Cookie (.*) "$1; Secure; HttpOnly; SameSite=Strict"
</IfModule>

JavaScript (NOT RECOMMENDED for session/auth cookies):

// ⚠️ Can only set Secure and SameSite via JS
// Cannot set HttpOnly (defeats the purpose)
document.cookie = "preference=dark_mode; Secure; SameSite=Lax; Max-Age=31536000; Path=/";

// For non-sensitive cookies only
function setSecureCookie(name, value, days) {
  const maxAge = days * 24 * 60 * 60;
  const secure = window.location.protocol === 'https:' ? '; Secure' : '';
  document.cookie = `${name}=${value}; SameSite=Lax${secure}; Max-Age=${maxAge}; Path=/`;
}

// ❌ NEVER for session/auth cookies - use server-side instead

Fix 5: SameSite Attribute Values

Choose the right SameSite value for your use case:

Strict (Most Secure):

Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict
  • Never sent on cross-site requests
  • Not even sent when clicking links from other sites
  • Best for session/auth cookies
  • May affect user experience (relogin after external links)

Lax (Balanced):

Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax
  • Sent on top-level GET navigation (clicking links)
  • Not sent on cross-site POST, images, iframes
  • Good default for most cookies
  • Balances security and usability

None (Required for cross-site):

Set-Cookie: widget=abc123; Secure; SameSite=None
  • Sent on all cross-site requests
  • MUST include Secure attribute
  • Required for embedded widgets, SSO, payment processors
  • Only use when absolutely necessary

Comparison:

Scenario Strict Lax None
Link from external site ❌ Not sent βœ… Sent βœ… Sent
Form POST from external site ❌ Not sent ❌ Not sent βœ… Sent
Iframe on external site ❌ Not sent ❌ Not sent βœ… Sent
Image on external site ❌ Not sent ❌ Not sent βœ… Sent
AJAX from same site βœ… Sent βœ… Sent βœ… Sent

Fix 6: WordPress-Specific

Add to wp-config.php:

<?php
// Secure session cookies
@ini_set('session.cookie_httponly', true);
@ini_set('session.cookie_secure', true);
@ini_set('session.use_only_cookies', true);

// For WordPress auth/session cookies, add to functions.php:
add_filter('send_auth_cookies', function($send) {
    // WordPress handles this, but ensure HTTPS is enforced
    if (!is_ssl() && !is_admin()) {
        wp_redirect('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 301);
        exit();
    }
    return $send;
});

Plugin approach:

// In theme functions.php or custom plugin
add_action('init', function() {
    if (!is_ssl() && !is_admin()) {
        wp_redirect('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 301);
        exit();
    }
});

// Cookie security plugin settings
// Recommended: "Really Simple SSL" plugin
// - Automatically adds Secure flag
// - Fixes mixed content
// - Easy configuration

Platform-Specific Guides

Detailed implementation instructions for your specific platform:

Platform Troubleshooting Guide
Shopify Shopify Cookie Security Guide
WordPress WordPress Cookie Security Guide
Wix Wix Cookie Security Guide
Squarespace Squarespace Cookie Security Guide
Webflow Webflow Cookie Security Guide

Verification

After implementing secure cookies:

  1. Check DevTools Application/Storage tab:

    • All session/auth cookies have Secure βœ…
    • All session/auth cookies have HttpOnly βœ…
    • All cookies have SameSite set βœ…
    • No warnings or yellow flags
  2. Test HTTP access (should fail):

    # Try to access cookie over HTTP
    curl -I http://example.com
    # Cookies should NOT be returned
    
    # HTTPS should work
    curl -I https://example.com
    # Cookies should be returned with security flags
    
  3. Verify JavaScript access blocked:

    // In browser console
    console.log(document.cookie);
    // Should NOT show HttpOnly cookies (session, auth)
    // Only non-sensitive cookies should appear
    
  4. Test cross-site requests:

    • Open DevTools β†’ Network tab
    • Visit external site linking to yours
    • Click link to your site
    • Check request headers
    • SameSite=Strict cookies should not be sent
    • SameSite=Lax cookies should be sent on GET
  5. Security scan verification:

    • Run SecurityHeaders.com scan
    • Run OWASP ZAP scan
    • Check for cookie security warnings
    • Should see improved security score

Common Mistakes

  1. Setting SameSite=None without Secure - Browsers reject this
  2. Using Secure flag over HTTP - Cookies won't be set
  3. Adding HttpOnly to functional cookies - Breaks JS that needs access
  4. Too strict SameSite - Users logged out when clicking external links
  5. Not updating subdomain cookies - Inconsistent security across subdomains
  6. Forgetting CSRF tokens - SameSite not enough for all CSRF protection
  7. Setting security flags client-side only - HttpOnly must be server-side
  8. No expiration time - Session cookies persist too long
  9. Testing only in development - Production HTTPS behaves differently
  10. Not documenting cookie purposes - Hard to determine correct security level

Session/Authentication Cookies:

  • Secure flag enabled
  • HttpOnly flag enabled
  • SameSite=Strict or SameSite=Lax
  • Reasonable expiration time (Max-Age)
  • Path restricted to minimum needed
  • Domain set appropriately
  • HTTPS enforced site-wide

Preference/Tracking Cookies:

  • Secure flag enabled (if HTTPS)
  • SameSite=Lax minimum
  • Longer expiration acceptable
  • Not HttpOnly (if JS needs access)

Third-Party Cookies:

  • Secure flag required
  • SameSite=None only if necessary
  • Review privacy implications
  • Consider cookie consent requirements

General:

  • All cookies over HTTPS
  • Regular security audits
  • Cookie policy documented
  • Compliance with regulations (GDPR, CCPA)

Session Cookies (Highest Security)

Set-Cookie: sessionId=xyz789; Secure; HttpOnly; SameSite=Strict; Path=/; Max-Age=3600

Authentication Tokens

Set-Cookie: authToken=abc123; Secure; HttpOnly; SameSite=Strict; Path=/; Max-Age=86400

CSRF Tokens

Set-Cookie: csrfToken=def456; Secure; SameSite=Strict; Path=/; Max-Age=3600

Note: NOT HttpOnly - JavaScript needs to read this to include in requests

Remember Me Tokens

Set-Cookie: rememberToken=ghi789; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=2592000

User Preferences

Set-Cookie: theme=dark; Secure; SameSite=Lax; Path=/; Max-Age=31536000

Note: Can omit HttpOnly if JS needs to read

Analytics/Tracking

Set-Cookie: _ga=GA1.2.123456789; Secure; SameSite=Lax; Path=/; Max-Age=63072000

Additional Resources

// SYS.FOOTER