
Last verified: October 25, 2026. Sources reviewed through October 24, 2026. Exploitation and CISA KEV status change quickly, so recheck the links in the Sources section before acting on anything time-sensitive.
On October 21, 2026, the PHP and Node.js projects each shipped security releases for flaws that can lead to remote code execution. This guide covers the critical PHP and Node.js vulnerabilities of October 2026 the way an engineering team needs them: the CVEs, the affected versions, what each fix changes, how to prove your own servers are patched, and what to do if you cannot patch today. Version numbers and scores come from the project advisories unless another source is named. Where sources disagree, or a claim is unconfirmed, the article says so.
Quick answer: On October 21, 2026, PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix CVE-2026-109618 and CVE-2026-109619 (CVSS v4.0 9.2), two unauthenticated remote code execution flaws in PHP core, nicknamed PHP2Shell. The same day Node.js released 22.23.4, 24.22.1 and 26.12.1, fixing 10 CVEs, including Brotli decoder flaw CVE-2026-111497. Upgrade the runtime, rebuild container images, restart every worker, then verify the version that is actually running.
Why it matters: the PHP bugs sit in memory management and error handling, code that almost every request touches. PHP’s advisory says there is no complete configuration workaround; updating is the only full fix. The Node.js bug lives in the Brotli library bundled with the runtime, so updating npm packages does not remove it.
Who is affected: any PHP installation that serves web requests (Apache mod_php, PHP-FPM, php-cgi, FrankenPHP, LiteSpeed), and any Node.js 22, 24 or 26 service that decompresses Brotli data it does not control. Node.js 20 and older, and PHP 8.1 and older, receive no regular fix from the upstream projects.
What to do today:
Inventory every PHP and Node.js runtime you run, including containers, workers, staging and vendor appliances.
Patch PHP to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (or a vendor package that lists the CVE fix), and Node.js to 22.23.4, 24.22.1 or 26.12.1.
Restart the actual web-serving process. A package upgrade on disk does not change a running worker.
Verify the running version, not the version on disk, and watch logs for the indicators in section 6.
The table below lists the verified sequence. Times are UTC where the source gives one.
Date | Event | Source |
|---|---|---|
2024 | A first external audit of PHP (Quarkslab) leads a core developer to spot early signs of the out-of-memory problem. No concrete path to code execution is found at that point. | PHP Foundation |
Aug 11, 2026 | Calif reports an unconditional RCE with the same root cause. Wiz and OpenAI report further attack vectors in the following weeks. | PHP Foundation |
Oct 20, ~15:00 | The two CVE IDs leak online with a wrong release time and a wrong CVSS score of 10.0. PHP publishes a pre-announcement at 19:30 and moves the release up by a day. | PHP Foundation |
Oct 20, ~20:00 | Calif mistakenly publishes a proof-of-concept video, later deleted. Copies keep circulating. | PHP Foundation |
Oct 21, 07:41 | Chainguard publishes patched images roughly six hours before the coordinated release, which PHP describes as breaking the embargo. | PHP Foundation |
Oct 21, ~14:00 | PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 are released. Node.js releases 22.23.4, 24.22.1 and 26.12.1 the same day. | PHP, Node.js |
Oct 21 | CISA urges patching and says it has not observed exploitation at the time of its alert. watchTowr reports exploitation attempts later that day. | Tenable, The Hacker News |
Oct 22 | Google, as CNA for the Brotli library, publishes the CVE record for CVE-2026-111497. Imperva publishes a working proof of concept on a lab build. | Rescana, The Hacker News |
Oct 23 | PHP publishes an informational advisory for a third issue, CVE-2026-115560, which is not fixed by the October 21 releases. | The Hacker News |
No source we reviewed links them technically. They are different bugs in different projects with different CVE numbering authorities: PHP’s flaws are in the Zend engine’s memory and error handling, while Node.js’s is in a bundled third-party compression library. They landed on the same day. The common thread is process, not code: AI-assisted vulnerability research is shortening the gap between discovery and public exploit tooling. Cognition’s write-up makes the same point, and the PHP Foundation notes that AI tools now help validate reports on the defender side too.
Both flaws are in php-src, the language runtime, not in WordPress, Laravel, Symfony or any plugin. Researchers call the pair PHP2Shell. PHP assigned the CVEs and scored each at 9.2 (Critical) on CVSS v4.0.
Class and CWEs | Memory corruption (use-after-free pattern). PHP lists CWE-416, CWE-755, CWE-824, CWE-1325 and CWE-1419. |
|---|---|
Root cause | PHP aborts a request that exceeds its memory limit by calling |
How OOM is reached | Multipart parsing can be pushed to exhaustion while staying inside apparent limits: a request without a usable declared length can slip past the |
Prerequisites | Network access to a PHP-served endpoint. No authentication and no user interaction. CVSS v4.0 marks attack complexity High and attack requirements Present. |
Confirmed impact | Unauthenticated RCE as the web worker account. PHP validated full RCE over HTTP against Apache mod_php with default input and memory limits on prefork configurations. Reliability depends on the PHP build, extensions, frontend behavior and allocator layout. |
By server API | mod_php: highest risk, because shutdown runs even when startup failed, before any application code. FPM: the child exits when startup fails, but PHP’s advisory says source analysis supports FPM as susceptible through application-stage paths, and states no complete FPM exploit had been demonstrated. The PHP Foundation’s release post says RCE was also confirmed on FPM through application-level code paths. FrankenPHP is likely susceptible by source analysis; LSAPI behaves like FPM and is likely but unvalidated. |
Fixed in | 8.2.35, 8.3.36, 8.4.27, 8.5.12. |
What the patch changes: it removes longjmp() for OOM. PHP now raises a delayed, graceful soft OOM at the next VM safe point, and an immediate hard exit through _exit() if memory use reaches a second, higher threshold. Shutdown no longer sees invalid state, and PHP keeps its ability to cap memory.
Class | Use-after-free through stale worker state. |
|---|---|
Root cause | A PHP worker keeps global state (error, session, object and extension state) across requests. Some of it points into request memory. If it is not reset, or is repopulated after reset, a later request on the same worker can touch freed memory before normal initialization replaces it. |
Notable detail | A warning, notice or deprecation raised while PHP applies per-directory or per-request INI settings reads the stale error state. Turning off |
Prerequisites | Network access, no authentication. Needs a worker that serves more than one request, which describes most production setups. |
Confirmed impact | PHP’s security team says unauthenticated RCE has been demonstrated against Apache mod_php and PHP-FPM. On FPM the path depends on a suitable application operation. Session lifecycle RCE was validated against both; the PHP Foundation says the reporter demonstrated RCE against a real application on mod_php. |
Fixed in | 8.2.35, 8.3.36, 8.4.27, 8.5.12. The fix resets the affected request-owned global state so it cannot survive into operations that run before the next request starts. |
PHP’s advisories enumerate the four supported branches. Google Cloud and Wiz go further and say every release back to PHP 5.4 is affected by at least one of the two flaws, and Calif’s analysis, as summarized by Tenable, traces the earliest affected code path to 5.4.0. Treat anything older than the fixed versions as vulnerable unless your vendor shows a package that lists the CVE fix.
PHP branch | Status | Affected | Fix |
|---|---|---|---|
8.5 | Supported | Before 8.5.12 | 8.5.12 |
8.4 | Supported | Before 8.4.27 | 8.4.27 |
8.3 | Supported | Before 8.3.36 | 8.3.36 |
8.2 | Security fixes end Dec 31, 2026 | Before 8.2.35 | 8.2.35 |
8.1, 8.0, 7.4 | End of life | Affected | No release. Source backport patches were published for these branches, per Cognition and third-party reports. Check distro or vendor packages. |
7.3 and older | End of life | Affected, per Google Cloud and Wiz | No upstream fix. Upgrade or isolate. |
Severity: 9.2 Critical on CVSS v4.0, set by PHP. Scores differ by scorer. NVD’s own analysis is reported at CVSS v3.1 9.8, and Red Hat rates the issues Important because RHEL defaults to PHP-FPM. Both are third-party figures, so confirm on the NVD and Red Hat records.
Exploitation: separate proof of concept from confirmed attacks.
Proof of concept: PHP’s security team said it found no functional exploits at the time of release, but the leaked Calif video sped up PoC development, and public PoC code now exists. We do not link it.
Attempts: watchTowr reports exploitation attempts from October 21 on its honeypot network, and Amazon reports honeypot activity from October 17. Several vendors describe very high volumes of probing.
Confirmed compromise: reports are inconsistent. The Hacker News said no source had confirmed a successful break-in as of the first day, and some national agencies later reported early signs of misuse. Treat unpatched, internet-facing PHP as under active attack without assuming any specific host is breached.
CISA KEV: neither CVE was in the Known Exploited Vulnerabilities catalog as of October 23, per Tenable and SOCRadar. Check the catalog directly.
PHP is blunt about this: updating is the only complete mitigation. If you cannot upgrade a binary today, the settings below come from PHP’s advisories and Google Cloud’s guidance. Set them in server-level configuration, never with ini_set(), .htaccess or .user.ini, then restart every worker.
; /etc/php/8.3/fpm/pool.d/www.conf (TEMPORARY, adjust path to your version)
; CVE-2026-109619: disable session upload progress (also covers CVE-2026-115560)
php_admin_flag[session.upload_progress.enabled] = off
; CVE-2026-109618: remove the predictable per-request limit PHP's own advisory targets.
; Revert to a finite value after patching, and pair with a cgroup limit (see below).
php_admin_value[memory_limit] = -1
; Shrink the multipart attack surface (also worth keeping after patching)
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M
php_admin_value[max_input_nesting_level] = 16
php_admin_value[max_input_vars] = 250
php_admin_value[max_file_uploads] = 10
; PHP 8.5 only, CVE-2026-109619
php_admin_value[fatal_error_backtraces] = 0
Do not use RLIMIT_AS, RLIMIT_DATA or ulimit -v to cap memory. They make mmap() fail, which triggers the vulnerable path.
A cgroup or container memory limit is a better cap than PHP’s own, with caveats. PHP says it is not guaranteed to prevent allocation failure, and killing a whole cgroup can take down the master and every worker.
At the proxy or WAF: block multipart/form-data on routes that do not accept uploads, enforce a request body cap (Google suggests client_max_body_size 8m; on NGINX) and reject chunked POSTs where you can. These controls are partial because the OOM bug can also be reached through application-specific paths.
Setting memory_limit = -1 removes your protection against runaway scripts. That is why it is a stopgap only.
On October 23, PHP published an informational advisory for CVE-2026-115560, a code execution path tied to session handling. Per the reporting we reviewed, it requires an unusual combination: session.serialize_handler set to php_binary or php_serialize in configuration, then switched to php with ini_set() before session_start(). PHP says it will handle the fix in a regular release and publish a full advisory then. As of October 23, PHP had not published affected version ranges, a CVSS score or a target release, and no exploitation was reported. The same session.upload_progress.enabled=0 setting is the recommended mitigation.
The October 21 Node.js release fixes 10 CVEs across the 26.x, 24.x and 22.x lines: four High, three Medium and three Low. It also updates bundled c-ares, Undici, OpenSSL (24.x and 22.x), SQLite (22.x) and the CA certificate store. The highest severity in every supported line is High, as rated by the Node.js project.
Component | The Brotli decoder bundled with Node.js, reached through |
|---|---|
Mechanism | An invalid block switch was reported as a temporary need for more input instead of a permanent format error. The streaming decoder kept appending input bytes to an eight-byte internal buffer and wrote past its end. CWE-787 (out-of-bounds write) and CWE-252 (unchecked return value) are listed. |
Impact | Node.js says malformed input can corrupt process memory, leading to a crash, disclosure of process memory, or remote code execution. |
Affected | Node.js 26.x before 26.12.1, 24.x before 24.22.1, 22.x before 22.23.4. End-of-life lines are always treated as affected and get no fix. |
Fixed in | Node.js 22.23.4, 24.22.1, 26.12.1. Upstream Brotli 1.0.10, 1.1.1 and 1.2.1 contain the library fix. |
Credit | Reported by OpenAI Codex Security (Khai Tran and Sylvie Mayer); fixed by Matteo Collina and Antoine du Hamel. |
Is it really RCE? Node.js’s own advisory title says the flaw permits remote code execution, so the classification has a primary source. The evidence on how reliable that is looks like this:
Imperva demonstrated code execution on the official Node.js 24.22.0 Linux arm64 image, and said it took multiple attempts. The patched 24.22.1 rejected the same inputs.
The first public PoC reportedly reached code execution only on a deliberately weakened build. On a standard build its author reported a crash and a limited read of process memory.
CISA’s assessment attached to the NVD record lists exploitation as none and automatable as no.
As of October 23, no source we reviewed reported exploitation in the wild, and the CVE was not in CISA KEV.
A fair reading: a denial of service from a crafted stream is the most reliable outcome, code execution is demonstrated in the lab but not shown to be reliable, and nothing says it is being used in attacks yet.
Is it remotely exploitable? Only where your service, or a library it uses, decompresses Brotli data an attacker controls. Imperva tested two routes: Node’s built-in fetch()reading a Brotli response from a hostile server, and Express 5 or body-parser 2 decoding a Brotli request body (Imperva names Express 5.0.0 to 5.3.0 and body-parser 2.0.0 to 2.3.0). A bare Node.js HTTP server does not decode incoming Brotli bodies on its own.
Severity: Node.js rates it High. Google, as CNA, scores it CVSS v4.0 9.2, and Singapore’s CSA cites a 9.8 CVSS v3.1 score. As of October 23, NVD’s own analysis was still pending, per Rescana. Scores differ by scorer, so use exposure (does anything decompress untrusted Brotli?) to prioritize.
CVE-2026-105482 (26.x, 24.x, 22.x): dns.resolve*() and dns.reverse()silently truncate hostnames containing a NUL byte, an incomplete fix of CVE-2026-48930. An app that validates a name against an allowlist and then resolves it can act on records for a different name.
CVE-2026-105483 (26.x, 24.x): tls.checkServerIdentity() did not recognize IPv4 hosts written with fullwidth digits, so a certificate with a numeric DNS name could authenticate an IP endpoint. A regression after the fix for CVE-2026-48618.
CVE-2026-106069 (26.x, 24.x, 22.x): a leading-zero IPv4 literal such as 0127.0.0.1 is normalized one way by the WHATWG URL rules and another by the OS resolver (on platforms such as macOS), so a certificate can be validated against a different IP than the one connected to.
These three matter most for services that connect to user-supplied hosts, such as webhook senders, link previewers and importers.
CVE | Severity | Issue | Lines |
|---|---|---|---|
CVE-2026-105481 | Medium | HTTP keep-alive socket can treat a response written before the request as valid | 26, 24, 22 |
CVE-2026-106072 | Medium | Non-canonical IPv4 hostnames let DNS SANs authenticate IP endpoints | 26, 24, 22 |
CVE-2026-106074 | Medium |
| 26, 24, 22 |
CVE-2026-106070 | Low |
| 26 |
CVE-2026-106071 | Low | Permission Model wildcard over-grant | 26 |
CVE-2026-106073 | Low | Permission Model bypass with | 26, 24, 22 |
These are separate problems with separate fixes. CVE-2026-111497 is in the runtime, so only a fixed Node.js release removes it. As of October 23, neither Express nor body-parser had published an advisory for it, per Rescana. On October 22, body-parser published a different advisory, GHSA-27r3-jxmh-p828, for a denial of service from trailing bytes after a compressed request body, fixed in body-parser 2.4.0 and 1.20.9. It touches the same compressed-body path, so apply it in the same change window. Where a service does not need compressed request bodies, body-parser’s documented inflate: false option rejects encoded bodies before any decompression stream is created. That covers inbound bodies only; fetched responses are covered only by the runtime patch.
One more item for teams in India and anywhere else that uses eMudhra-issued certificates: the official release lists a CA-store update for the eMudhra intermediate CA compromise. Test outbound TLS to any endpoint whose certificate chains to eMudhra in staging before you roll out.
Only details confirmed in the official advisories are included. The four High-severity Node.js CVEs are shown; the other six are in the table above.
CVE | Runtime | Vulnerability type | Severity | Affected versions | Attack prerequisites | Fixed version | Official source |
|---|---|---|---|---|---|---|---|
CVE-2026-109618 | PHP | Memory corruption via OOM handling (CWE-416, 755, 824, 1325, 1419) | Critical, CVSS v4.0 9.2 | Before 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12; older branches per vendors | Network, no auth; PHP-served endpoint; highest risk on mod_php | 8.2.35, 8.3.36, 8.4.27, 8.5.12 | |
CVE-2026-109619 | PHP | Use-after-free via stale global state | Critical, CVSS v4.0 9.2 | PHP 8.2 to 8.5 before the same fixed versions; older branches per vendors | Network, no auth; worker handling multiple requests; FPM path needs a suitable app operation | 8.2.35, 8.3.36, 8.4.27, 8.5.12 | |
CVE-2026-111497 | Node.js | Out-of-bounds write in bundled Brotli decoder (CWE-787) | High (Node.js); CVSS v4.0 9.2 (CNA) | 26.x, 24.x, 22.x before fix; EOL lines unfixed | App decompresses attacker-controlled Brotli via node:zlib | 22.23.4, 24.22.1, 26.12.1 | |
CVE-2026-105482 | Node.js | DNS name truncation at NUL byte | High | 26.x, 24.x, 22.x | App validates a hostname, then resolves it with dns.resolve*() | 22.23.4, 24.22.1, 26.12.1 | |
CVE-2026-105483 | Node.js | TLS hostname bypass (fullwidth-digit IP) | High | 26.x, 24.x | Connects to attacker-influenced IP host; certificate with numeric DNS name | 24.22.1, 26.12.1 | |
CVE-2026-106069 | Node.js | TLS hostname bypass (leading-zero IPv4) | High | 26.x, 24.x, 22.x | Connects to non-canonical IPv4 literal on a platform whose resolver reads it differently | 22.23.4, 24.22.1, 26.12.1 |
The most common failure here is checking the wrong PHP. The php on your command line, the PHP-FPM pool behind NGINX, the Apache module and the PHP inside a container can all be different installs with different versions and different php.ini files. Check the one that serves traffic.
# Every PHP binary on the box, and the CLI version (CLI only!)
which -a php php-fpm php-fpm8.3 php8.3 2>/dev/null
php -v | head -1
# What is actually running web traffic?
ps -eo pid,user,lstart,cmd | grep -E 'php-fpm|apache2|httpd|frankenphp|lsphp|php-cgi' | grep -v grep
# Version and server API of the FPM binary (adjust the name to your version)
php-fpm8.3 -v | head -1Expected output: the first line of php -v ends in (cli), and the FPM binary’s ends in (fpm-fcgi). If those show different versions, you have two installs and need to patch both. For Apache mod_php, apachectl -M | grep php shows the module.
Debian and Ubuntu backport fixes without changing the upstream version. Cognition’s example: the fixed Ubuntu 24.04 package is 8.3.6-0ubuntu0.24.04.13 and Ubuntu 22.04’s is 8.1.2-1ubuntu2.28, so php -v still prints 8.3.6 or 8.1.2. Ask the package manager instead:
# Debian/Ubuntu: does the changelog mention the CVEs?
apt-get changelog php8.3-fpm | grep -E 'CVE-2026-109618|CVE-2026-109619'
# RHEL family
rpm -q --changelog php-fpm | grep -E 'CVE-2026-109618|CVE-2026-109619'
# Installed PHP packages and versions
dpkg-query -W -f='${Package} ${Version}\n' 'php*'No output from the grep means the installed package does not list the fix. Google Cloud also warns that third-party mirrors can lag behind coordinated disclosure; use your distribution’s official security repositories or official source builds.
composer audit --locked # advisories for packages in composer.lock
composer check-platform-reqs # checks the CLI environment onlycomposer audit examines the packages in your lockfile. The PHP binary is not one of them, so a clean audit says nothing about CVE-2026-109618. Changing require.phpor config.platform.php in composer.json does not patch anything either; those settings do not choose the PHP that runs in production.
# Version inside an image (use --entrypoint if your image overrides it)
docker run --rm your-registry/app:tag php -v | head -1
docker run --rm your-registry/app:tag php-fpm -v | head -1
# Pull fresh base layers; a cached floating tag may not have moved
docker pull php:8.3-fpm
# Scan an image for the CVEs
trivy image --severity HIGH,CRITICAL your-registry/app:tag | grep -E 'CVE-2026-109618|CVE-2026-109619'
# Every image running in a Kubernetes cluster
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{" "}{end}{"\n"}{end}'Images pinned by digest need a new digest, not just a tag change. Scanners that match on version strings can mislabel backported distro packages in either direction, so confirm against the vendor advisory.
# Listening sockets; a FastCGI port (often 9000) should never face the network
sudo ss -tlnp | grep -E ':(80|443|8080|8443|9000)\b'
# Settings forced at pool level
grep -RnE 'php_admin_(value|flag)' /etc/php/*/fpm/pool.d/
# Effective values for the FPM binary (does not include pool overrides)
php-fpm8.3 -i | grep -E '^(memory_limit|post_max_size|session.upload_progress.enabled)'# "(deleted)" means the binary on disk was replaced after this process started
for pid in $(pgrep -f 'php-fpm: master'); do echo "$pid -> $(sudo readlink /proc/$pid/exe)"; done
# Apache mod_php: look for a replaced libphp in the parent process
sudo grep -E 'libphp.*\(deleted\)' /proc/$(pgrep -o apache2)/maps
# Debian/Ubuntu: services still using pre-upgrade libraries
sudo needrestart -r lAny (deleted) result means that process still runs the old code. Restart php8.x-fpm, apache2, httpd, frankenphp or lsws explicitly, plus queue workers and long-running runtimes such as Laravel Octane. Google Cloud’s guidance adds one forensic point: if a host shows signs of active probing, capture worker memory before restarting, because a restart destroys volatile evidence.
Signals worth alerting on, from Google Cloud’s detection table: sh, bash, curl or wgetspawned by php-fpm, httpd, apache2 or frankenphp; PHPSESSID cookies longer than 256 bytes in responses; bursts of HTTP 500 and 200 responses on the same POST endpoint; and spikes in FPM worker respawns with exit code 139.
node -v # the node on your PATH, not necessarily the service's
which -a node
ps -eo pid,user,lstart,cmd | grep '[n]ode'
# The binary a specific running process uses (replace 1234)
sudo readlink /proc/1234/exe
$(sudo readlink /proc/1234/exe) -vProcess managers, systemd units and version managers (nvm, fnm, asdf) can all point a service at a different Node than the one in your shell. The last two lines remove the guesswork.
Save this as check-node-fix.js and run it with the same binary your app uses:
const fixed = { 22: [22, 23, 4], 24: [24, 22, 1], 26: [26, 12, 1] };
const v = process.versions.node.split('.').map(Number);
const target = fixed[v[0]];
if (!target) {
console.log(`Node ${process.version}: no fix exists for this line. Move to 22, 24 or 26.`);
process.exit(2);
}
const patched = v[1] > target[1] || (v[1] === target[1] && v[2] >= target[2]);
console.log(`Node ${process.version}: ` + (patched ? 'patched for the Oct 21 release' : `VULNERABLE, upgrade to ${target.join('.')}`));
process.exit(patched ? 0 : 1);Exit code 0 means patched, 1 means vulnerable, 2 means the release line has no fix. Do not rely on process.versions.brotli to judge patch status. Node.js applied the upstream fix as a cherry-pick, so the reported library version may not change.
docker pull node:24-slim && docker run --rm node:24-slim node -v
# Every running container
for c in $(docker ps -q); do printf '%s: ' "$(docker inspect -f '{{.Name}}' $c)"; docker exec $c node -v 2>/dev/null || echo '(no node)'; done
kubectl exec deploy/my-api -- node -vgrep -RInE '^FROM[[:space:]].*node|node-version|setup-node|"node":' \
--include='Dockerfile*' --include='*.yml' --include='*.yaml' --include='package.json' \
--include='.nvmrc' --include='.tool-versions' .
# Where might Brotli be decoded?
grep -RInE 'brotliDecompress|createBrotliDecompress|zlib\.brotli|express\.(json|raw|text)|bodyParser' src/ \
--include='*.js' --include='*.ts' --include='*.mjs'A lockfile pins npm packages, not the runtime. The runtime version lives in your Dockerfile FROM line, .nvmrc, CI config, Helm values or serverless runtime setting. The grep for Brotli usage gives you a starting list, not proof; middleware and libraries can decode Brotli without any of those strings appearing in your code.
npm audit is not enoughnpm audit --omit=dev
npm ls body-parser expressnpm audit compares the packages in your lockfile against an advisory database. It does not inspect the Node.js binary, so it will not flag CVE-2026-111497, and a clean result says nothing about runtime exposure. Run it for dependency risk such as the body-parser advisory above, and run the version check for runtime risk. You need both.
After deploying, run node -v inside the live container or host, or expose the version from a health endpoint your team controls. Expected results are v22.23.4 or higher on the 22 line, v24.22.1 or higher on 24, and v26.12.1 or higher on 26.
Inventory applications and runtimes. Search Dockerfiles, base images, distro packages, source builds, CI (setup-php, setup-node, .nvmrc, .tool-versions), Helm values, Terraform, Packer, Lambda runtimes and Ansible, Chef or Puppet. Include staging, cron hosts and queue workers. Agent tooling can automate this step. Cognition published an open-source Vulnerability Fixes plugin for exactly this inventory; its own guidance is to review any skill before running it, because skills run with your agent’s full permissions.
Identify vulnerable deployments. Use the section 6 and 7 checks. Rank by exposure: internet-facing mod_php first, then internet-facing FPM, then services that decompress untrusted Brotli or connect to user-supplied hosts, then internal systems.
Review the official advisories. Read the PHP and Node.js pages, not a summary. Note the fixed versions for your exact release line.
Test in staging. A patch release inside the same line (8.3.30 to 8.3.36, 24.21.0 to 24.22.1) is usually low risk but still needs a run of your suite. Compare the old and new images for extensions, ini settings, OpenSSL and ICU versions and base OS.
Check dependency and framework compatibility. Frameworks do not patch runtimes, but they can break on a jump between minor versions. Services on end-of-life runtimes face a bigger migration; plan it separately and use a vendor backport as a bridge, not a destination.
Update runtimes and images. Rebuild with fresh base layers and pin the patched version.
# PHP on Debian/Ubuntu
sudo apt-get update && sudo apt-get install --only-upgrade 'php8.3*'
sudo systemctl restart php8.3-fpm # and apache2 if you run mod_php
# Containers
docker build --pull --no-cache -t your-registry/app:2026-10-25 .
# Dockerfile: pin a patched tag, for example FROM node:24.22.1-slimRun regression and security tests. Exercise upload endpoints, session handling, outbound TLS calls and compressed-body paths specifically, because those are what these CVEs touch.
Deploy with a controlled rollout. Canary a small share of traffic, watch error rate and latency, then widen. Restart queue workers and schedulers in the same release, or they keep running old code.
Verify production runtime versions. Re-run the section 6 and 7 checks against live hosts and containers, not the build output.
Monitor logs and behavior. Watch for child processes spawned by PHP workers, outbound connections from web servers, unexpected 500/200 bursts and worker crashes. Keep the heightened watch for at least a few weeks, since patched hosts may already have been touched before the update.
Rolling back means reopening the hole. Keep the previous image digest, but treat a rollback to an unpatched runtime as a temporary state with the WAF rules and settings from section 3 in place. Prefer fixing forward.
Restarts cost something. FPM restarts drop in-flight requests, and upload-heavy apps feel it most. Drain nodes behind the load balancer first, and restart in batches.
Extension breakage is the usual surprise. PECL extensions compiled for an older minor version fail to load on a newer one. Test the full extension list.
If you cannot patch within hours, consider restricting ingress to your WAF only, or taking the service offline. Google Cloud suggests both for unpatched public resources, and CERT-EU advised isolating internet-facing PHP applications.
Run a supported branch (8.3, 8.4 or 8.5); plan the exit from 8.2 before December 31, 2026.
Prefer PHP-FPM behind a reverse proxy over mod_php; PHP rates mod_php the highest risk.
Run workers as an unprivileged user with read-only document roots (ProtectSystem=strict in systemd, readOnlyRootFilesystem: true in Kubernetes) and NoNewPrivileges=yes.
Keep display_errors off and log_errors on, and keep expose_php off.
Block outbound internet from web servers and stop PHP workers from spawning shells or download tools.
Set conservative post_max_size, max_input_vars and max_file_uploadspermanently.
Run only Active or Maintenance LTS lines. The Node.js project recommends this for production, and Node.js 20 reached end of life on April 30, 2026.
Pin the runtime version in Dockerfile, .nvmrc and CI so every environment matches.
Reject compressed request bodies you do not need (inflate: false or a proxy rule), and cap decompressed size.
Validate hostnames to canonical form before connecting to user-supplied hosts.
Run npm audit and a runtime version check in CI, as two separate gates.
Rebuild images on a schedule, not only when you change code, and use --pull.
Pin base images by version and digest, and update the digest on every security release.
Use minimal base images, a non-root user and a read-only root filesystem.
Generate an SBOM per image and store it with the image.
Scan images on build and on a recurring schedule in the registry, since new CVEs appear after you ship.
Know which platform layers are patched for you and which are yours. Google Cloud, for example, patched App Engine standard on supported PHP versions automatically, while App Engine flexible and Cloud Run services without automatic base image updates needed a rebuild and redeploy.
Apply least privilege to instance and workload identities so a compromised web worker cannot reach other resources.
Keep secrets in a managed secrets store, not in environment files baked into images.
Restrict egress and put internet-facing apps behind a WAF with managed rules and request-body limits.
Turn on exposure and vulnerability scanning in your cloud security tooling and review findings for externally exposed workloads.
Fail builds on a defined severity threshold for both dependency and image scans.
Give pipelines short-lived credentials and scope tokens per repository.
Pin third-party actions and plugins by version or commit.
Produce and archive an SBOM on every build so you can answer “where do we run this runtime?” in minutes.
Automate patch pull requests for base images and runtime versions, with a human reviewing risk.
Assuming a framework update patches the runtime. Updating Laravel, Symfony, WordPress or Express changes packages, not the PHP or Node.js binary underneath.
Trusting php -v. It reports the CLI, and on Debian and Ubuntu the banner stays the same after a backported fix.
Checking CLI PHP while production runs a different FPM. Two installs, two versions. Patch and verify the one behind the web server.
Failing to restart processes. Upgrading packages on disk leaves vulnerable php-fpm or libphp.so resident in running workers.
Trusting an old Docker base image or a floating tag. php:8.3-fpm or node:24only moves when you pull and rebuild.
Running npm audit as the only Node.js check. It does not look at the runtime binary.
Ignoring unsupported runtimes. Node.js 20 and older, and PHP 8.1 and older, get no regular upstream fix. “It still runs” is not a security plan.
Overlooking staging, workers and vendor appliances. Staging often mirrors production exposure, queue workers run long-lived processes, and control panels and appliances can bundle their own PHP or Node.
Treating a temporary workaround as a permanent fix.session.upload_progress.enabled=0 does nothing for CVE-2026-109618, and PHP says no setting closes every path.
Using memory limits that trigger the bug. RLIMIT_AS and ulimit -v make allocation fail, which is the vulnerable path.
Confusing publicly accessible with confirmed exploitable. Exposure sets your patch order. It is not proof of compromise, and a lack of alerts is not proof of safety. Check logs and host telemetry.
Keep a runtime inventory you can query. The hardest question this week was “where do we run PHP or Node.js?” Teams with an SBOM per build and a service catalog answered in minutes. Teams without one searched repositories by hand.
Shorten the patch cycle for runtimes. A 30-day production patch cycle does not fit a flaw that has proof-of-concept code within a day. Define an emergency path with named approvers and a target in hours for internet-facing, critical-severity runtime fixes.
Stay close to current runtimes. Cognition observed that teams already on the latest PHP and Node.js lines had the easiest week, while teams one or two generations behind faced compatibility work under pressure. Upgrade debt is security debt.
Assign ownership. Every service needs a named owner who is accountable for its runtime version, and platform engineering needs authority to rebuild and redeploy shared base images.
Reduce attack surface. Decommission unused apps, and move internal tools behind a VPN or zero-trust gateway so “internet-facing” stays a short list.
Rehearse the restart. Know how to drain, restart and verify without an outage, and practice it before the next release lands.
Fund the upstream. The PHP Foundation notes that core security work depends on a small group of funded developers. If your business runs on PHP, sponsorship is a risk-management line item.
This article summarizes public advisories and vendor research. Akoode Technologies did not discover, independently test or patch these vulnerabilities. What follows is how our engineering practice treats the problems they illustrate. Akoode’s leadership team brings over a decade of experience in AI consulting and technology development, and teams in Gurugram India and USA work in stacks that include Node.js and PHP.
Secure architecture from the first sprint. Authentication, authorization, input limits and least-privilege service accounts are design decisions, not late additions. Our custom software development work starts there.
Code review with a security lens. Reviews check upload handling, session use, outbound requests to user-supplied hosts and decompression paths, the exact areas these CVEs touch.
Dependency and runtime management. Pinned runtimes, lockfiles, scheduled rebuilds and scanning in CI, so a security release becomes a routine change. For web platforms this includes tracking runtime support dates.
Cloud and delivery security. Containerized deployments, restricted egress, managed secrets and repeatable pipelines are part of our cloud and DevOps practice.
Monitoring and long-term maintenance. Shipping is the start. Logging, alerting and a patch cadence keep a product safe over years. Where a client needs extra hands for a runtime migration, staff augmentation can fill the gap.
Akoode has delivered 180+ projects across 15+ industries and holds a 4.9 Google rating from 126 reviews and a 5.0 rating on GoodFirms. You can see the kinds of systems we build in our case studies. If you want a second pair of eyes on your PHP or Node.js estate, book a call with Akhil.
PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 on October 21, 2026, fixing CVE-2026-109618 and CVE-2026-109619, both rated Critical (CVSS v4.0 9.2). A third issue, CVE-2026-115560, was disclosed on October 23 with limited details and no emergency fix.
PHP’s advisories list releases before 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Google Cloud and Wiz say every release back to PHP 5.4 is affected. PHP 8.1 and older get no upstream release, though source backports exist for 7.4, 8.0 and 8.1.
Identify the PHP that serves web traffic (FPM, mod_php, FrankenPHP), then check the installed package changelog for the CVE IDs instead of trusting php -v. Confirm the running process was restarted after the upgrade.
An unauthenticated remote code execution flaw caused by memory corruption when PHP runs out of memory during request startup. Request cleanup then treats partly initialized data as valid. It is fixed in 8.2.35, 8.3.36, 8.4.27 and 8.5.12, and mod_php carries the highest risk.
A use-after-free in which request-owned global state survives after its memory is freed, and a later request on the same worker uses it. PHP demonstrated unauthenticated RCE against mod_php and PHP-FPM. The same four PHP releases fix it.
Only when an application decompresses attacker-controlled Brotli data, for example a fetched response or a request body decoded by middleware. Node.js says it can lead to a crash, memory disclosure or RCE. Lab code execution has been shown; no in-the-wild exploitation was reported as of October 23.
Update Node.js 22 to 22.23.4, 24 to 24.22.1 and 26 to 26.12.1. Node.js 20 and older, plus 23 and 25, are end of life and receive no fix, so move them to a supported line.
No. Laravel, Symfony and WordPress are applications and libraries running on PHP. These flaws are in the PHP runtime, so only a patched PHP build, plus a restart, fixes them.
No. npm audit checks the packages in your lockfile. It does not inspect the Node.js binary, so verify the runtime version separately against 22.23.4, 24.22.1 or 26.12.1.
Yes, if the image contains an affected runtime. A container does not isolate the application from a bug in the interpreter inside it. Rebuild with a patched base image, redeploy, and check the version inside the running container.
PHP does not require it, but for internet-facing PHP you cannot patch quickly, restricting ingress to your WAF or taking the service offline is defensible, and Google Cloud and CERT-EU describe both. If you can patch within hours, patch and restart instead.
Check the installed package changelog or version against the fixed releases, confirm the web-serving process started after the upgrade, and run php-fpm -v or node -vinside the live container or host.
PHP security announcement: php.net, October 21, 2026
GitHub advisories: GHSA-w8hw-w9fx-j79w (CVE-2026-109618), GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
PHP security team post: oss-security, CVE-2026-109619
Release notes: PHP 8.5.12, 8.4.27, 8.2.35
Google Cloud: Responding to core PHP vulnerabilities and Wiz Research: PHP2Shell
Node.js: October 21, 2026 Security Releases; release notes for 22.23.4, 24.22.1, 26.12.1
Third-party analysis used for exploitation status and cross-checks: Rescana (Node.js), Tenable (PHP), The Hacker News, Singapore CSA
Check live status: CISA Known Exploited Vulnerabilities catalog
Compiled from the public advisories and research listed above on October 25, 2026. This article is informational and is not a substitute for your vendor’s security advisories or your own incident response process.
Subscribe to the Akoode newsletter for carefully curated insights on AI, digital intelligence, and real-world innovation. Just perspectives that help you think, plan, and build better.