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?
answer
- httponly and secure are off by default
- samesite empty means no attribute
- strict mode off by default
- before session_start(), or a warning
- 8.4 deprecated sid_length and trans_sid changes
basics
~10 sTurn 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 sPHP'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; 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 = 0go deeper
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.
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.
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.
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