Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

Sessions and Cookies in PHP

Sessions turn stateless HTTP into a login that lasts across several pages. Here is how PHP handles this internally, which defaults apply and what session.upload_progress has to do with PHP2Shell.

Last updated 5 min readPHP knowledge

In short: HTTP remembers nothing between two requests. A PHP session solves this: PHP assigns a random session ID, sends it to the browser as a cookie and stores the associated data on the server, by default as a file. On every further request, session_start() reads the data back using the ID. The PHP session also includes the function session.upload_progress, which php.net recommends temporarily switching off on the server side because of PHP2Shell.

Why web applications need sessions

Every request to a web server stands on its own. Without an additional mechanism, a shop would no longer know, when you click "shopping cart", who just put something into it, and an admin area would have to ask for the password on every click. Sessions join individual requests into one session.

For decision-makers this means: after login, the session ID is worth as much as username and password combined. For admins it means: where and how session data is stored is a security question, especially on servers with several websites.

What happens at session_start()

The process is the same on every request and can be described in five steps:

  1. The browser sends along the cookie with the session name, by default PHPSESSID.
  2. session_start() reads the ID and passes it to the storage handler, by default files.
  3. The handler loads the stored data, PHP converts it back with the serialize_handler and fills $_SESSION.
  4. The script reads and changes $_SESSION like a normal array.
  5. At the end of the request, PHP writes the data back. With session.lazy_write (default: on), only if it has changed.
<?php
session_start();

$_SESSION['besuche'] = ($_SESSION['besuche'] ?? 0) + 1;
echo 'Seitenaufrufe in dieser Sitzung: ', (int) $_SESSION['besuche'];

If there is no session yet, PHP generates a new ID and sets the cookie. If session.use_strict_mode is active, PHP rejects unknown IDs supplied by the browser and generates a new one. The PHP manual describes this setting as mandatory for basic security.

Defaults worth knowing

According to the PHP manual, the following values apply when nothing else is configured. Many distributions and hosting providers set their own values, so the configuration that is actually effective always counts.

Setting Default Meaning
session.save_handler files storage location of the data, Redis or database also possible
session.name PHPSESSID name of the cookie
session.serialize_handler php format of the stored data
session.use_strict_mode 0 unknown IDs are accepted
session.use_only_cookies 1 no session ID in the address
session.cookie_httponly 0 JavaScript can read the cookie
session.cookie_secure 0 cookie also over unencrypted HTTP
session.cookie_samesite empty no SameSite attribute
session.gc_maxlifetime 1440 seconds until possible clean-up
session.gc_probability / gc_divisor 1 / 100 clean-up at roughly every hundredth start
session.upload_progress.enabled 1 upload progress is written into the session

The values for cookie_httponly, cookie_secure and use_strict_mode are therefore not designed for security out of the box. How to set them correctly is described in the guide Securing sessions in PHP. The settings session.sid_length and session.sid_bits_per_character have been marked as deprecated since PHP 8.4.0.

Setting cookies directly

Besides the session cookie, an application can set its own cookies. Since PHP 7.3.0, setcookie() accepts an array of options for this, including samesite. Since PHP 8.5.0 there is additionally the option partitioned.

<?php
setcookie('sprache', 'de', [
    'expires'  => time() + 60 * 60 * 24 * 30,
    'path'     => '/',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);

According to the manual, if samesite equals None, secure must also be set, otherwise the browser blocks the cookie. Cookies are data from the browser: their content comes back on the next request via $_COOKIE and must be checked like any other input. More on this on the page Superglobals.

session.upload_progress: progress display for uploads

During a file upload, PHP can write the progress into the session. For this, the form sends a hidden field with the name from session.upload_progress.name, by default PHP_SESSION_UPLOAD_PROGRESS. PHP then stores the data in $_SESSION under a key with the prefix upload_progress_, with details such as start time, total size and bytes already processed.

Important for understanding: according to the manual, PHP writes this information into the session before any script is executed. The function therefore works while the request is being processed, before the application code. Its settings are of the type INI_PERDIR and cannot be changed at runtime.

Sessions and PHP2Shell: the interim measure

One of the interim measures against the PHP2Shell flaws named by php.net is to set session.upload_progress.enabled=0 in php.ini, in the FPM pool or in the server configuration and then restart all workers, explicitly not via ini_set() or .user.ini. On 23 October 2026 the notice on CVE-2026-115560 was added, with the same remedy until the next regular bugfix release.

; FPM pool of the website, then restart all workers
php_admin_flag[session.upload_progress.enabled] = off

According to trade reports, this setting alone does not close both flaws. The update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 remains mandatory. Whether your application uses upload progress at all and how to set the setting permanently is described in the article Switching off session.upload_progress.

How does PHP SOS help?

  • We read out the session settings that are actually effective for each website, not just the values from the command line.
  • We set the PHP2Shell interim measures per pool, restart all workers and check the result.
  • We separate session storage locations and pool users between websites as part of server hardening.
  • After an incident we invalidate all existing sessions so that stolen session IDs become worthless.

Frequently asked questions

What is the difference between a cookie and a session?

A cookie is a small data record that the browser stores and sends along with every request. A session stores the actual data on the server; the cookie contains only a random identifier, the session ID.

How long does a PHP session live?

By default the session cookie is valid until the browser is closed. The data on the server may be cleaned up after session.gc_maxlifetime, by default 1440 seconds without access. When that actually happens depends on the system's clean-up logic.

What does session.upload_progress do?

During a file upload, the function writes the progress into the session so that a second request can display it. It is active by default. Because of PHP2Shell, php.net recommends temporarily switching it off on the server side.

Is it enough to switch off session.upload_progress with ini_set()?

No. According to the manual, the setting cannot be changed at runtime anyway, and php.net explicitly names php.ini, the FPM pool or the server configuration as the right places, not ini_set() or .user.ini. Then restart all workers.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help