CWE-614: Sensitive Cookie Without 'Secure' Flag - PHP
Overview
The Secure attribute on an HTTP cookie instructs the browser to only transmit that cookie over encrypted HTTPS connections. Without this attribute, the browser also sends the cookie on plaintext HTTP requests. If a user is directed to an HTTP version of a page - by following an HTTP link, via a DNS rebinding attack, or because the site does not enforce HTTPS - the session cookie travels in the clear and can be intercepted by any observer on the network path.
In PHP, setcookie() does not set the Secure flag by default. The same applies to PHP session cookies: session_start() uses whatever settings are in php.ini, which typically does not have session.cookie_secure = 1 enabled unless explicitly configured.
Primary Defence: Pass 'secure' => true in the options array to every setcookie() call. Configure session.cookie_secure = 1 in php.ini (or via session_set_cookie_params() before session_start()). Combine with httponly and samesite for defence-in-depth.
Common Vulnerable Patterns
setcookie() Without Secure Flag
<?php
// VULNERABLE - cookie sent over HTTP as well as HTTPS
setcookie('auth_token', $tokenValue, time() + 3600, '/');
// Legacy five-parameter form - easy to miss security parameters
setcookie('session_pref', $preference, time() + 86400, '/', '', false, true);
// ^------- secure (FALSE!)
Why this is vulnerable:
- Omitting the
secureparameter (which defaults tofalse) means the browser will include this cookie in HTTP requests. An attacker on the same network can capture theauth_tokencookie and impersonate the user.
session_start() Without Secure Cookie Parameters
<?php
// VULNERABLE - session cookie not configured as Secure
session_start(); // Uses php.ini defaults; session.cookie_secure defaults to 0
// Later the session ID cookie is sent on both HTTP and HTTPS requests
$_SESSION['user_id'] = $userId;
Why this is vulnerable:
- The PHP default
session.cookie_secure = 0sends thePHPSESSIDcookie over HTTP. A compromised network path exposes the session identifier, allowing session hijacking.
setcookie() Using Positional Parameters
<?php
// VULNERABLE - positional form makes it easy to accidentally set secure=false
setcookie(
'remember_me', // name
$token, // value
time() + 2592000, // expires
'/', // path
'', // domain
false, // VULNERABLE: secure = false
true // httponly = true
);
Why this is vulnerable:
- The positional (pre-PHP 7.3) syntax places
secureat parameter position 6. Accidentally passingfalseor omitting it means the cookie is not protected.
Secure Patterns
setcookie() with Options Array (PHP 7.3+)
<?php
// SECURE: options array makes each attribute explicit and readable
setcookie('auth_token', $tokenValue, [
'expires' => time() + 3600,
'path' => '/',
'domain' => '', // current host only
'secure' => true, // HTTPS only
'httponly' => true, // not accessible via JavaScript
'samesite' => 'Strict', // not sent on cross-site requests (CSRF protection)
]);
Why this works:
- The options array form (PHP 7.3+) makes every cookie attribute explicitly named, eliminating positional mistakes. Setting
'secure' => trueensures the browser only sends the cookie over HTTPS.
Session Cookie Security
<?php
// SECURE: configure session cookie before session_start()
session_set_cookie_params([
'lifetime' => 0, // session cookie (deleted on browser close)
'path' => '/',
'domain' => '',
'secure' => true, // HTTPS only
'httponly' => true,
'samesite' => 'Strict',
]);
session_start();
// Regenerate session ID after authentication to prevent fixation
session_regenerate_id(true);
$_SESSION['user_id'] = $authenticatedUserId;
Why this works:
- Calling
session_set_cookie_params()beforesession_start()configures thePHPSESSIDcookie with theSecureflag.session_regenerate_id(true)prevents session fixation attacks by replacing the session ID after login.
php.ini Configuration (Recommended for All Sites)
; php.ini - enforce secure cookie settings at the server level
session.cookie_secure = 1 ; Session cookies HTTPS only
session.cookie_httponly = 1 ; Session cookies not accessible via JS
session.cookie_samesite = Strict ; Prevent CSRF via cross-site requests
; Optional: restrict session cookies to HTTPS scheme in the cookie name
session.name = __Secure-PHPSESSID ; Browser-enforced Secure flag requirement
Why this works:
php.inisettings apply to all PHP scripts on the server, providing a baseline security configuration. The__Secure-cookie name prefix instructs the browser to reject the cookie if it arrives without theSecureattribute, adding a redundant enforcement mechanism.
Remediation Steps
Locate the Finding
- Source: Any
setcookie()call,session_start()without priorsession_set_cookie_params() - Sink: Missing
'secure' => trueinsetcookie()options;session.cookie_secure = 0inphp.ini
Apply the Fix
- PRIORITY 1: Update all
setcookie()calls to use the options array with'secure' => true - PRIORITY 2: Add
session_set_cookie_params(['secure' => true, 'httponly' => true, 'samesite' => 'Strict'])before everysession_start()call - PRIORITY 3: Set
session.cookie_secure = 1inphp.inias a server-wide baseline
Verify the Fix
- Use browser developer tools -> Application -> Cookies; confirm the
Securecolumn is checked for all sensitive cookies - Navigate to the HTTP version of the app and confirm the session cookie is not present in the request
- Rescan with the security scanner to confirm the finding is resolved
Check for Similar Issues
Search for: setcookie(, session_start(, review each call for secure => true
Testing
- Normal input: sign in over HTTPS and confirm session, authentication, and preference cookies are set and accepted.
- Boundary input: test local development, staging behind a proxy, and redirects from HTTP to HTTPS to ensure cookie behavior is intentional.
- Malicious input: make an HTTP request to the same host and verify sensitive cookies are absent from the request headers.