Skip to content

fix: disable session.upload_progress for new and existing sites - #246

Merged
mrrobot47 merged 4 commits into
EasyEngine:developfrom
mrrobot47:fix/disable-session-upload-progress
Oct 21, 2026
Merged

mrrobot47 merged 4 commits into
EasyEngine:developfrom
mrrobot47:fix/disable-session-upload-progress

Conversation

@mrrobot47

@mrrobot47 mrrobot47 commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

PHP's 2026-10-21 security advisory for CVE-2026-109618 (GHSA-w8hw-w9fx-j79w) and CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp), announced at https://www.php.net/archive/2026.php#2026-10-21-1, names session upload progress as an attack path: "Session lifecycle RCE was validated against mod_php and FPM". The official mitigation is: "Set session.upload_progress.enabled=0 and restart every worker. Use php.ini, an FPM pool or server configuration that applies before request parsing. Do not rely on ini_set() or FPM .user.ini." PHP also recommends keeping it off after patching if it isn't used.

This PR applies that mitigation to every WordPress site. It is defence in depth, not a substitute for the patched PHP images (8.2.35, 8.3.36, 8.4.27 and 8.5.12). WordPress doesn't use PHP sessions, so nothing changes for WordPress itself. PHP 8.1 and older get no official release with the fix, so for sites still on those versions this setting is the main protection until they move to a supported version.

The setting goes into each site's custom.ini, which php-fpm loads from its scanned conf.d at startup, before any request is parsed. Running sites then get a graceful php-fpm reload (kill -USR2 1, as ee site reload does), which re-executes the master and replaces every worker.

New sites: php.ini.mustache and php.ini-56.mustache set it to Off. PHP-type sites also take their custom.ini from this package's template (SITE_WP_TEMPLATE_ROOT in site-type-php's PHP.php), so this covers new PHP sites too.

Existing sites: container migration 20261021120000_site-type-wp_disable_wp_session_upload_progress.php adds the setting to config/php/php/conf.d/custom.ini of every WordPress site, enabled or disabled, on every PHP version:

  • A file that already sets session.upload_progress.enabled globally (On or Off) is left alone. The check uses PHP's own ini parser in the same mode as php.ini, and only entries before the first section whose name starts with PATH or HOST count, since PHP scopes everything after such a header to it (even under later ordinary headers). So a commented-out line, a wrong-case key or a value set only in a scoped section doesn't count, and a scoped override is kept as it is; for a file with a syntax error, only the lines above it count, as in PHP.
  • The file is edited in place, so owner, mode and inode stay the same (macOS bind-mounts the single file). CRLF line endings and a UTF-8 BOM are kept, and the rest of the file is unchanged. The setting goes at the very top, so neither a [PATH=]/[HOST=] section nor a multi-line quoted value can capture it. A missing custom.ini is recreated with the owner of its conf.d. A symlinked custom.ini or a directory in its place is skipped with a warning.
  • The original files are kept in /opt/easyengine/.backup/session-upload-progress-wp-<timestamp>/<site>/custom.ini. If a later step of the upgrade fails (another container migration or the image migration), down() restores them byte for byte and reloads php-fpm of the running sites.
  • Running sites get the same graceful php-fpm reload as ee site reload (kill -USR2 1) once the image migration is done. Sites whose php container the image migration recreated already read the new file, so they're not reloaded again. Disabled sites pick it up when they're enabled.
  • A problem with one site (including a failed docker-compose ps when looking up its php container) is a warning that names the site and the manual fix, so it doesn't block the upgrade.

To undo it on a site, remove the line from custom.ini (or set it to On), then run ee site reload <site>.

On macOS, custom.ini is currently bind-mounted outside the conf.d directory PHP scans (a separate, older issue), so the setting has no effect there yet; that needs its own fix.

Testing: a scripted matrix over the ini editing and the migration against site directories (empty, whitespace-only, the templates, other settings, no final newline, already Off, explicit On, commented out, wrong case, CRLF, BOM, [HOST=]/[PATH=] sections with and without a scoped setting, a multi-line quoted value containing a [ line, a syntax error, missing file, missing conf.d, symlink, directory, 0444 file owned by www-data, immutable file, disabled site, stopped container, failed container lookup, failure halfway through, unwritable backup dir, second run, down()), with each result also parsed by real PHP, on PHP 7.4 and 8.5. A single-file bind mount into a running php8.4 container picks up the edit and the reload. A live upgrade from 4.13.2 with WordPress sites on PHP 8.4 and 7.4 and PHP sites on 8.1, 5.6 and 8.4 (one disabled, one with an explicit On, one with extra user settings): FPM reports the setting off on every changed site, the explicit On stays, sites keep serving 200, multipart and WordPress REST media uploads work, the disabled site gets it on ee site enable, a second run changes nothing, and a failed image migration restores every custom.ini exactly.

Needs a core release that pins this tag together with site-type-php's matching change.

The session upload progress handler is not needed by WordPress or most apps; turning it off removes it as an attack surface (php2shell hardening). Apps that need it can set it back to On in custom.ini.
Adds session.upload_progress.enabled = Off to the custom.ini of every wp site, enabled or disabled, unless it already sets it. Files are edited in place (owner, mode and inode kept), originals are backed up under EE_BACKUP_DIR and restored if the upgrade rolls back. Running sites get a graceful php-fpm reload after the image migration, skipped for php containers it recreated; disabled sites pick it up when enabled. Per-site problems are warnings so they don't block the upgrade.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Scoped settings and silent container-discovery failures can leave the intended security mitigation unapplied.

2 open issues
What changed in this PR

Adds PHP session upload-progress hardening to WordPress site provisioning and existing-site upgrades.

Changes:

  • Disables upload progress in both PHP configuration templates.
  • Adds a migration with backups, rollback, and conditional PHP-FPM reloads.
File Description
templates/​config/​php-fpm/​php.ini.mustache Disables upload progress for new sites.
templates/​config/​php-fpm/​php.ini-56.mustache Applies the same default for PHP 5.6.
migrations/​container/​20261021120000_site-type-wp_disable_wp_session_upload_progress.php Updates existing WordPress sites and manages restoration and reloads.

Review effort: Balanced

…ns set it

custom.ini is now read with PHP's own ini parser (same mode as php.ini) to find a global session.upload_progress.enabled, so a value set only inside a [PATH=] or [HOST=] section no longer counts. The new lines go at the very top of the file, where neither such a section nor a multi-line quoted value can capture them; for a file with a syntax error only the lines above it count, matching what PHP applies. A failed docker-compose ps is now a warning with the reload command instead of being treated as a stopped container.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Scope detection can mistake scoped settings for global overrides and skip sites that need the mitigation.

1 open issue
2 resolved since last review

Review effort: Balanced

PHP treats any section whose name starts with PATH or HOST (case-insensitive, spaces allowed before the =) as scoped, and entries under a later ordinary header stay in that scope. Only the part before the first such header now counts as global, so a scoped-only setting no longer stops the global Off from being added.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

CRLF scoped-only configurations are misclassified as global settings, causing affected sites to miss the mitigation.

1 open issue
1 resolved since last review

Review effort: Balanced

@mrrobot47
mrrobot47 merged commit 8373911 into EasyEngine:develop Oct 21, 2026
3 checks passed
@mrrobot47
mrrobot47 deleted the fix/disable-session-upload-progress branch October 21, 2026 15:19
mrrobot47 added a commit to EasyEngine/easyengine that referenced this pull request Oct 21, 2026
Switches off session.upload_progress for new sites and, through a container migration, for every existing WordPress and PHP site on all PHP versions (EasyEngine/site-type-wp#246, EasyEngine/site-type-php#120).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants