Laravel Session Security: Fix Insecure Cookie Config (Secure, HttpOnly, SameSite)

Laravel session cookies missing Secure, HttpOnly, or SameSite flags? Fix your config/session.php to prevent session hijacking, cookie theft, and CSRF attacks.

High severity Application Security Updated 2026-03-01 Markdown

HTTP is a stateless protocol — each request is independent with no inherent notion of identity. Sessions solve this by storing state server-side and giving the client a session cookie that acts as a pointer to that state. The session cookie is effectively the key to your application: whoever possesses a valid session cookie is, from the application's perspective, the legitimate authenticated user. Every aspect of session cookie configuration directly affects whether that key can be stolen.

The three cookie flags address three distinct attack vectors. Secure prevents the session cookie from being transmitted over plain HTTP, closing the SSL-stripping vector where a network attacker downgrades connections on shared networks. HttpOnly blocks JavaScript from reading the cookie value via document.cookie, meaning an XSS vulnerability cannot be leveraged to steal sessions. SameSite=Lax prevents the cookie from being sent with cross-origin requests initiated by third-party sites, providing a browser-native CSRF defense that complements CSRF tokens.

Session fixation is a related but distinct attack: instead of stealing an existing session, the attacker sets a known session ID before the user logs in (by making the victim visit a URL with a ?PHPSESSID=known-value parameter, for example), then uses that ID after authentication to hijack the authenticated session. Laravel's built-in authentication calls $request->session()->regenerate() on login, preventing this. Custom authentication code frequently skips this step and remains vulnerable. The combination of all these defenses — proper cookie flags, session ID regeneration, and server-side session storage with Redis or database — provides comprehensive protection.

The Problem

Insecure session configuration allows attackers to steal or manipulate user session cookies through network interception, cross-site scripting, or cross-site request attacks. When session cookies lack the Secure, HttpOnly, and SameSite flags, they can be transmitted over unencrypted connections, accessed by JavaScript, or sent with cross-origin requests, all of which enable session hijacking.

How to Fix

  1. 1

    Set secure session options in config/session.php

    Update your config/session.php with these security settings:

    return [
        'driver' => env('SESSION_DRIVER', 'database'),
        'lifetime' => 120,
        'expire_on_close' => false,
        'encrypt' => true,
        'cookie' => env('SESSION_COOKIE', 'app_session'),
        'path' => '/',
        'domain' => env('SESSION_DOMAIN'),
        'secure' => true,
        'http_only' => true,
        'same_site' => 'lax',
    ];
  2. 2

    Use database or Redis for session storage

    File-based sessions can be exposed if the storage directory is accessible. Use database or Redis:
    # .env
    SESSION_DRIVER=database
    # Create the sessions table
    php artisan session:table
    php artisan migrate
    Or use Redis for better performance:
    # .env
    SESSION_DRIVER=redis
    REDIS_HOST=127.0.0.1
    REDIS_PORT=6379

    Both options provide better security than file storage and support multi-server deployments.

  3. 3

    Regenerate session ID after authentication

    Regenerate the session ID after login to prevent session fixation attacks. Laravel does this automatically with its built-in authentication, but if you have custom auth:

    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => 'required|email',
            'password' => 'required',
        ]);
    if (Auth::attempt($credentials)) {
            $request->session()->regenerate();
            return redirect()->intended('/dashboard');
        }
    return back()->withErrors(['email' => 'Invalid credentials.']);
    }
  4. 4

    Set appropriate session lifetime

    Do not set excessively long session lifetimes. In config/session.php:

    'lifetime' => 120, // 2 hours, reasonable for web apps
    'expire_on_close' => false,

    For high-security applications (banking, healthcare):

    'lifetime' => 15,
    'expire_on_close' => true,

    Consider implementing idle timeout with JavaScript that warns users before session expiration.

How to Verify

Log in to your application and inspect the session cookie in your browser developer tools (Application > Cookies). Verify these flags are set:

- Secure: Yes (checkmark) - HttpOnly: Yes (checkmark) - SameSite: Lax or Strict

Also test with curl:

curl -I https://yourdomain.com/login

Look for Set-Cookie header with: Secure; HttpOnly; SameSite=Lax

Prevention

Include secure session settings in your base Laravel project template. Test cookie flags in your CI pipeline. Review session configuration during security audits. Use StackShield to monitor that session cookies maintain proper security flags across deployments.

Frequently Asked Questions

What does each cookie flag do?

Secure ensures the cookie is only sent over HTTPS, preventing interception on unsecured networks. HttpOnly prevents JavaScript from reading the cookie, blocking XSS-based session theft. SameSite=Lax prevents the cookie from being sent with cross-origin requests (with exceptions for top-level navigation), mitigating CSRF attacks.

Should I use SameSite Strict or Lax?

Lax is the recommended default. It allows the cookie to be sent when users navigate to your site from external links (important for user experience) but blocks it on cross-origin AJAX requests and form submissions. Strict blocks all cross-origin cookie sending, which can break login flows from email links or OAuth callbacks.

Does encrypting the session cookie help?

Yes. Setting encrypt to true in session.php encrypts the session cookie value using your APP_KEY. Even if an attacker intercepts the cookie, they cannot read or modify the session data without the encryption key. Laravel encrypts cookies by default through the EncryptCookies middleware.

Which session driver should I use in production?

Use database or redis — never file in production. The file driver stores sessions as individual files that are affected by filesystem permissions and exposed if the storage directory is misconfigured. The database driver stores sessions in your existing database with proper access controls. Redis is the best choice for performance and for multi-server deployments where all servers need to share session state.

How do I handle session security in a multi-subdomain application?

Set SESSION_DOMAIN=.yourdomain.com (note the leading dot) in your .env to allow the session cookie to be shared across subdomains like app.yourdomain.com and api.yourdomain.com. Be aware that this also allows any subdomain to read the session cookie, so ensure all subdomains are trusted. Never set SESSION_DOMAIN to a value broader than your own domain.

Related Security Terms

Free security check

Is your Laravel app exposed right now?

34% of Laravel apps we scan have at least one critical issue. Most teams don't find out until something breaks. Our free scan checks your live application in under 60 seconds.

18% have debug mode on
72% missing security headers
12% have exposed .env
Scan My App Free No signup required. Results in 60 seconds.