skip to content

In PHP 8.5, which session ini settings do you change from their defaults to harden the session cookie, and where do you set them?

level: middleimportance: should knowfreq 45%

answer

  1. httponly and secure are off by default
  2. samesite empty means no attribute
  3. strict mode off by default
  4. before session_start(), or a warning
  5. 8.4 deprecated sid_length and trans_sid changes

basics

~10 s

Turn on session.cookie_httponly, session.cookie_secure, session.use_strict_mode and set session.cookie_samesite, all off or empty by default. Set them in php.ini, or at runtime before session_start() via session_set_cookie_params() or session_start() options.

solid answer

~30 s

PHP's session cookie ships soft: `session.cookie_httponly` and `session.cookie_secure` default to off, `session.cookie_samesite` is empty so no `SameSite` attribute is sent, and `session.use_strict_mode` is `0`. Hardening means `cookie_httponly=1`, `cookie_secure=1` on HTTPS, `cookie_samesite="Lax"` or `"Strict"`, and `use_strict_mode=1`. Leave `session.use_only_cookies=1` and `session.use_trans_sid=0` alone: changing either toward URL-borne IDs has been deprecated since PHP 8.4, as has changing `sid_length` or `sid_bits_per_character`. Set them in php.ini so every entry point gets them; at runtime you can use `ini_set()`, `session_set_cookie_params([...])` or `session_start(['cookie_httponly' => 1])`, but only **before** the session starts. Once it is active PHP warns that the settings cannot be changed.

code

ini · 8 lines
ini
; php.ini
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = "Lax"
; leave the defaults below untouched (changing them is deprecated since 8.4)
session.use_only_cookies = 1
session.use_trans_sid = 0

go deeper

for a junior

Recall that the session cookie's httponly, secure and samesite settings are off or empty by default and that you turn them on in php.ini.

for a middle

Name each directive with its default, the four places to set it, the rule that all of them must run before session_start(), and the PHP 8.4 deprecations.

for a senior

Show how you guarantee the settings in every environment, spot pre-8.4 advice such as longer sid_length, and verify the real Set-Cookie header in a deployed response.

for a principal

Argue for one enforced baseline in server config over per-application calls, and decide how exceptions such as SameSite=None for embedded flows get reviewed.

## The defaults are compatibility defaults The session module's built-in values favour "works everywhere" over "safe by default". The shipped `php.ini-production` and `php.ini-development` files carry the same values for these settings, so a stock PHP 8.5 install sends a session cookie with none of the protective attributes. | Directive | Built-in default | Hardened value | Why | |---|---|---|---| | `session.cookie_httponly` | `0` | `1` | keeps the ID away from page scripts | | `session.cookie_secure` | `0` | `1` (HTTPS sites) | never sends the ID over plain HTTP | | `session.cookie_samesite` | empty (no attribute) | `"Lax"` or `"Strict"` | limits cross-site sending | | `session.use_strict_mode` | `0` | `1` | refuses IDs the server never issued | | `session.use_only_cookies` | `1` | leave at `1` | IDs never come from the URL | | `session.use_trans_sid` | `0` | leave at `0` | IDs never get written into links | | `session.cookie_lifetime` | `0` | usually `0` | cookie lasts until the browser closes | What each cookie attribute means to a browser is HTTP-level knowledge; the PHP-level knowledge is the directive names, their soft defaults and where to set them. ## Settings you should not touch PHP 8.4 deprecated the knobs that only ever weakened sessions or that storage backends had to cope with: - **Disabling** `session.use_only_cookies` or **enabling** `session.use_trans_sid` now raises `E_DEPRECATED`; so does any non-empty `session.referer_check`, and changing `session.trans_sid_tags` or `session.trans_sid_hosts`. The `SID` constant is deprecated too. - Setting `session.sid_length` to anything but `32`, or `session.sid_bits_per_character` to anything but `4`, raises `E_DEPRECATED`. The value still applies while it is in range, but the direction is to accept 32-character IDs and stop tuning them. A candidate who recommends lengthening `sid_length` as a hardening step is describing pre-8.4 advice. ## Where to set them 1. **php.ini (preferred).** One place, applied before any script runs, so no entry point can forget. When writing `None` for SameSite, quote it: the php.ini comment warns that a bare `none` is read like `false`. 2. **`ini_set()` before `session_start()`.** Works for all of these directives, which are changeable at runtime. 3. **`session_set_cookie_params()` before `session_start()`.** Takes an options array with the keys `lifetime`, `path`, `domain`, `secure`, `httponly`, `samesite` and, since PHP 8.5, `partitioned`. 4. **`session_start()` options.** Any session directive without the `session.` prefix: `session_start(['cookie_secure' => 1, 'use_strict_mode' => 1])`. A key that does not map to a real `session.*` directive produces a warning that setting the option failed. ## Timing: before the session is active Every one of these routes must run **before** the session starts and before headers are sent: - `session_set_cookie_params()` on an active session warns "Session cookie parameters cannot be changed when a session is active" and returns `false`. - `ini_set()` of a session directive on an active session warns "Session ini settings cannot be changed when a session is active". - After output, both complain that headers have already been sent. Frameworks and front controllers usually solve this once, in a bootstrap step that runs before any session code. ## PHP 8.5 addition: partitioned PHP 8.5 added a `partitioned` key to `session_set_cookie_params()`, `session_get_cookie_params()` and `session_start()` for partitioned (CHIPS) cookies. It needs `secure` as well: with the session cookie marked partitioned but not secure, PHP emits a warning when it builds the cookie. Most first-party applications leave it off; it matters for sessions used inside third-party embeds. ## Verifying the result Configuration that lives in several layers (php.ini, per-directory overrides, bootstrap code) is easy to get subtly wrong, so check what actually happens: - `session_get_cookie_params()` returns the array PHP will use for the cookie: `lifetime`, `path`, `domain`, `secure`, `httponly`, `samesite` and, in PHP 8.5, `partitioned`. Log or assert it in a smoke test. - `ini_get('session.use_strict_mode')` confirms the setting a request really runs with. - The authoritative check is the response itself: inspect the `Set-Cookie` header for the session cookie in a deployed environment and confirm each attribute is present. A common surprise is an environment where HTTPS terminates in front of PHP: `cookie_secure=1` is still correct there, because the browser talks HTTPS even if PHP does not.

  • Why must session_set_cookie_params() be called before session_start()?
    The cookie parameters are used when `session_start()` builds the `Set-Cookie` header. Once a session is active, `session_set_cookie_params()` warns "Session cookie parameters cannot be changed when a session is active" and returns `false`, so the cookie keeps the old attributes. Put the call, or better the php.ini settings, in bootstrap code that runs first.
  • Is raising session.sid_length still a good hardening step in PHP 8.5?
    No. Since PHP 8.4, setting `session.sid_length` to anything but 32 or `session.sid_bits_per_character` to anything but 4 raises `E_DEPRECATED`. The value still applies within its range, but the supported direction is the default 32-character ID, with storage backends expected to accept it.

saying these in an interview costs you the question

  • PHP sends the session cookie with HttpOnly and Secure by default
  • session_set_cookie_params() can update the cookie after session_start()
  • Enabling session.use_trans_sid is a fine fallback for cookieless clients
  • Raising session.sid_length is the modern way to strengthen session IDs
  • session_start() option keys need the full 'session.' prefix