rfc:session_security_defaults

PHP RFC: Secure Session Configuration Defaults

Introduction

Three ini settings in PHP's session extension have security implications but ship with defaults that leave applications unnecessarily exposed. This RFC proposes changing those defaults so that new PHP installations are secure out of the box, without requiring application code changes for the common case.

The affected settings are:

  1. session.use_strict_mode — reject unrecognised session IDs supplied by the client
  2. session.cookie_httponly — exclude the session cookie from the JavaScript cookie API
  3. session.cookie_samesite — restrict cross-site delivery of the session cookie

Each change is a single-line edit to ext/session/session.c. No new ini entries, functions, or constants are introduced.

Background

Session Fixation (use_strict_mode)

Session fixation allows an attacker to pre-plant a known session ID and wait for a victim to authenticate against it. With use_strict_mode=0 (the current default), session_start() accepts any well-formed ID supplied in a cookie, $_GET, or $_POST and begins a session under that ID without verifying that the ID already exists in storage.

With use_strict_mode=1, php_session_initialize() calls s_validate_sid() on the proposed ID before accepting it. The built-in files handler checks whether a session file for that ID exists on disk; because an attacker-planted ID has no corresponding file, a fresh random ID is generated instead.

The HttpOnly cookie attribute, defined in RFC 6265 §4.1.2.6 and supported by all browsers since IE 6, instructs the browser to exclude the cookie from the document.cookie API. A script running in the page — whether first-party or injected by an XSS attack — cannot read or exfiltrate an HttpOnly cookie.

PHP has supported session.cookie_httponly since PHP 5.2.0 (2006) and the flag has been universally recommended by security guidance (OWASP, Mozilla, NIST) ever since. There is no legitimate reason to default it to off.

The SameSite attribute controls whether the browser attaches a cookie to cross-site requests. Without it, a forged form or link on an attacker-controlled site can trigger authenticated requests using the victim's session.

PHP's session.cookie_samesite defaults to an empty string, which emits Set-Cookie with no SameSite attribute. Browsers have progressively tightened their handling of such cookies: Chrome applied SameSite=Lax as the default starting with Chrome 80 (February 2020), and Firefox followed with version 103 (August 2022). Setting the attribute explicitly in PHP aligns the Set-Cookie header with browser behaviour and removes reliance on per-browser inference.

Setting SameSite=Lax on session cookies is recommended by (OWASP, Mozilla, NIST).

Comparison with Other Platforms

Platform Session cookie HttpOnly default SameSite default
PHP PHPSESSID ''0'' (off) not set
Java (Tomcat 10) JSESSIONID ''true'' not set
.NET Framework ASP.NET_SessionId ''false'' (off) not set
ASP.NET Core .AspNetCore.Session ''true'' ''Lax''
Ruby (Rack) rack.session ''true'' not set
Python (Django) sessionid ''true'' ''Lax''
Node.js (express-session) connect.sid ''true'' not set
Go (gorilla/sessions) configurable ''false'' (off) not set
PHP (Laravel) laravel_session ''true'' ''Lax''
PHP (Symfony) PHPSESSID ''true'' ''Lax''

Proposal

Change the following three default values in ext/session/session.c:

INI setting Old default New default
session.use_strict_mode 0 1
session.cookie_httponly 0 1
session.cookie_samesite Lax

Backward Incompatible Changes

session.use_strict_mode = 1

Applications that deliberately supply an externally controlled session ID — for example, a shared-secret hand-off between subdomains using the files save handler — will have the ID rejected because no matching session file exists at the time of the first request. The correct approach is to write and close the session on the originating side (session_write_close()) before presenting the ID to the receiving side.

Applications using a custom session handler whose validateId() method returns true unconditionally are unaffected. This includes common backends such as Redis and Memcached when using the default php-memcached or phpredis session handlers, which do not implement validateId() and therefore fall back to returning true.

session.cookie_httponly = 1

The HttpOnly flag is enforced by the browser. It prevents the session cookie value from being read via document.cookie; it has no effect on whether the browser sends the cookie with requests.

Applications that read the session cookie value from document.cookie in JavaScript (for example, to embed the ID in a custom request header) will no longer be able to do so. The session ID is a server-side credential and should not be consumed by client-side code. Applications that need a client-accessible token for request correlation should use a separate, explicitly non-HttpOnly CSRF token rather than the session cookie itself.

session.cookie_samesite = Lax

With SameSite=Lax, the browser sends the session cookie on same-site requests and on top-level cross-site navigations using safe HTTP methods (GET, HEAD). It does not send the cookie on cross-site sub-resource requests or cross-site POST requests.

Applications that rely on cross-site POST carrying the session cookie — for example, SP-initiated SAML SSO flows or legacy cross-origin form submissions — must either set SameSite=None; Secure explicitly for those endpoints or migrate to a token-based flow.

As noted above, Chrome and Firefox already apply Lax as the implicit default for cookies sent without a SameSite attribute. Applications running on those browsers are already subject to this behaviour; the change makes it explicit and consistent across all browsers and PHP versions.

Browser support

The SameSite cookie attribute is supported across all modern browsers:

Browser SameSite support since Lax-by-default since Reference
Chrome 51 (May 2016) 80 (February 2020) Chrome blog
Firefox 60 (May 2018) 103 (August 2022) MDN
Safari 12.1 (March 2019) not applied WebKit blog
Edge 18 (October 2018) 85 (August 2020) Edge docs

Safari supports the attribute and respects an explicit SameSite=Lax value but does not apply Lax as an implicit default for cookies lacking the attribute. Setting the attribute explicitly in PHP therefore closes the cross-browser gap and removes reliance on per-browser inference.

Proposed PHP Versions

PHP 8.6.

Vote

As per the voting RFC, a Yes/No vote with a 2/3 majority is required for each item. Each change is voted on separately so that the internals list can accept a subset.

  1. Change session.use_strict_mode default to 1: Yes / No
  2. Change session.cookie_httponly default to 1: Yes / No
  3. Change session.cookie_samesite default to Lax: Yes / No

Implementation

Patch to the PHP_INI_BEGIN() block in ext/session/session.c. No PR exists yet.

References

rfc/session_security_defaults.txt · Last modified: by jorg_sowa