PHP2Shell explained: the October 2026 PHP vulnerabilities
A practical guide to PHP2Shell: the two critical PHP flaws, patched versions, hosting checks, WAF limits, and what to verify before calling the incident closed.
Node2Shell explained: the Brotli flaw, fixed Node.js versions, other October 21 security fixes, and practical checks for containers, workers and hosting.

You update the dependencies. CI turns green. Someone posts a Node.js security advisory in the team chat, and suddenly “we deployed this morning” needs a few follow-up questions.
Which Node binary did you deploy? Did the worker get it too? Is the container still running yesterday's image?
A green tick is reassuring. It is not an inventory.
The useful answer first: the 21 October 2026 security releases are 22.23.4, 24.22.1 and 26.12.1. Update the appropriate supported release line, replace or restart the affected services, and verify what is actually running. These are the fixes published for this release, not a promise that they will remain the newest versions forever. Node.js 22 release, Node.js 24 release, Node.js 26 release.
This article was researched on 23 October 2026. It explains the evidence and proposes a practical response; I have not reproduced the exploit or inspected your infrastructure.
Node2Shell is Imperva Red Team's name for its analysis of CVE-2026-111497, a flaw in the Brotli decoder bundled with Node.js. The Node.js advisory rates it High and says an out-of-bounds write can permit remote code execution. Keep the attribution attached when sharing it: an alarming headline and a vendor's severity label are different things. Official Node.js advisory.
Brotli compresses data so fewer bytes travel over the network. Decoding unpacks it again. Node exposes Brotli operations through its node:zlib module. The distinction matters: this issue concerns the decoder, so simply turning off compression of your own outgoing pages does not establish that you have removed every decoding path. Node.js compression documentation.
An out-of-bounds write means code writes outside the memory region it was supposed to use. Remote code execution means an attacker may cause the process to execute their instructions. Your JavaScript can be neatly typed while a native library underneath it has a memory-safety flaw. TypeScript helps with a different class of mistakes.
Imperva reports code execution on the official Node.js 24.22.0 Linux arm64 image through two paths: a compressed response consumed by built-in fetch(), and incoming compressed bodies handled by configured Express/body-parser middleware. Its tests used selected configurations, not every release in the package ranges discussed. Execution required multiple attempts; the same inputs were rejected by 24.22.1. Those are meaningful results with clear limits, not proof that every Node application is equally exploitable. Imperva's original Node2Shell research.
The upstream Brotli change adds an earlier invalid-stream check and returns a format error instead of continuing through that condition. For readers who want to inspect the underlying change, use the upstream patch and Brotli security release. You do not need to run a public exploit against production to justify installing a published security fix.
| Your Node.js release line | October 21 fixed release | Release status at publication |
|---|---|---|
| 22.x | 22.23.4 | LTS |
| 24.x | 24.22.1 | LTS |
| 26.x | 26.12.1 | Current |
The linked 22.x, 24.x and 26.x release notes confirm these versions. You do not need to jump from 22 to 26 merely to obtain this security release. A supported patch update and a major-version migration are different changes with different testing costs.
Node.js 20, 23 and 25 are listed as end-of-life. The project does not issue normal security updates for EOL lines. Move to a supported line; if a distributor or commercial provider supplies backports, obtain its specific advisory and package-build evidence rather than guessing from the upstream version string. Node.js EOL policy and version list.
The phrase “we're on an old stable version” needs care here. Stable can describe your deployment habits. It does not extend a project's maintenance calendar.
Brotli attracts the headlines, but the release is broader. This compact map cross-checks the individual security advisories against the 22.x, 24.x and 26.x release notes.
| CVE | Area | Severity | Lines |
|---|---|---|---|
| CVE-2026-111497 | Brotli decoder memory write / RCE | High | 22, 24, 26 |
| CVE-2026-105482 | NUL-containing DNS names | High | 22, 24, 26 |
| CVE-2026-105483 | TLS checks with fullwidth IP digits | High | 24, 26 |
| CVE-2026-106069 | TLS checks with loose IP forms | High | 22, 24, 26 |
| CVE-2026-105481 | Premature keep-alive response | Medium | 22, 24, 26 |
| CVE-2026-106072 | Certificate checks for non-canonical IPv4 | Medium | 22, 24, 26 |
| CVE-2026-106074 | Randomness caches in snapshots/SEAs | Medium | 22, 24, 26 |
| CVE-2026-106070 | Invalid ZIP-header handling | Low | 26 |
| CVE-2026-106071 | Permission wildcard over-grant | Low | 26 |
| CVE-2026-106073 | fs.realpathSync permission bypass | Low | 22, 24, 26 |
There is a source wrinkle worth preserving: the individual advisory labels CVE-2026-106072 Medium, while the release notes group it with CVE-2026-106069 under a High-labelled TLS change. The table follows the individual advisory, rather than silently treating the grouped release-note label as a separate rating.
Bundled components also change. For example, the 22.x release updates c-ares, OpenSSL, SQLite, Undici and root certificates; the 24.x notes also list dependency and certificate updates. Review the notes for your exact line rather than assuming every branch contains an identical dependency list. 22.23.4 changes, 24.22.1 changes.
Start with the data entering your process, not the framework badge on the homepage.
Express 5 documents Brotli support. Middleware that inflates request bodies is therefore worth reviewing, including where it sits relative to authentication. If decompression happens before your authentication handler, “the endpoint requires login” does not by itself answer who can reach the decoder. That is a configuration question to investigate, not a claim about every Express app. Express 5 migration guide.
Also list services that fetch external URLs, import remote content or process compressed files. Check the HTTP client and decompression behaviour actually used. A worker with no public port can still consume untrusted input. The following is a suggested inventory, not a list of confirmed vulnerable products:
| Component | Question to answer |
|---|---|
| Public API | Which middleware inflates incoming bodies, and when? |
| URL importer or background worker | Can outside content reach a Brotli decoder? |
| Image or document processing service | Which runtime and libraries process remote responses? |
| CI/build job | What Node binary executes the build and its tools? |
| Old deployment or preview | Can it still receive traffic or run scheduled jobs? |
A source search can help locate calls such as brotliDecompress or createBrotliDecompress, both documented by Node.js. An empty search is not proof of safety: dependencies may call them for you.
npm audit checks your package dependency tree against vulnerability information. It remains useful, but a clean result does not certify the Node executable underneath the application. Updating Express or deleting node_modules is not a substitute for replacing an affected runtime. npm audit documentation.
Think of the deployment as three inventories: application packages, runtime, and operating-system/container components. Each needs its own evidence. Running the same check three times does not turn it into three different checks. Computers are very patient with that sort of optimism.
This command reports the Node process your current shell launches:
node -p 'JSON.stringify({node:process.version, executable:process.execPath, brotli:process.versions.brotli})'process.version identifies Node, process.execPath identifies its executable, and process.versions exposes component versions. A Brotli version string alone is not proof of patch status when a vendor has cherry-picked a fix. Node.js process documentation.
Now ask the harder question: is that the same binary your service uses? A version manager, service unit, container or hosting platform may select another one.
For a container called web, this is a useful inventory check:
docker exec web node -p 'process.version'It starts a new Node process inside that container. It does not interrogate the already-running application process. To close that gap, record the application runtime at startup, for example:
console.info("runtime", {
node: process.version,
executable: process.execPath,
pid: process.pid,
});Keep this in internal logs. Do not create a public diagnostic endpoint or dump environment variables. Pair the startup record with a deployment identifier, replica identity and timestamp so you can tell the new process from yesterday's log entry.
The following is my proposed workflow. Adapt the commands and rollout method to your system; they are not instructions I have executed against your production services.
Include web processes, workers, scheduled tasks, build runners, preview environments and artifacts that bundle a runtime. Give each one an owner. If ownership is unknown, write “unknown” rather than letting an empty spreadsheet cell masquerade as completed work.
On a host, use your supported installation channel. In a container build, update the base image or pinned digest in the relevant stages. Check the final runtime stage as well as the builder.
Docker's --pull checks for a newer base image; --no-cache reruns build steps. They solve different problems. Neither turns a deliberately pinned vulnerable version into a patched one. Update the reference first, then rebuild and test. Docker build guidance.
Deploy the rebuilt artifact and complete the rollout across replicas and workers. A registry containing a fixed image is not evidence that your service uses it.
If you use a monorepo, check every deployment target independently. Shared source code does not guarantee a shared runtime or release schedule.
Use normal test data to check sign-in, one important write, external API calls, background jobs and compressed-content handling where applicable. Watch for unexpected exits and TLS errors after the change. Keep a recovery plan, but do not automatically roll back to the vulnerable runtime to make a failing health check turn green.
Here is a fictional example of a useful completion record:
| Component | Evidence | Status |
|---|---|---|
| Web service | New artifact deployed; startup logs show fixed runtime on every replica | Verified |
| Worker | Image rebuilt; no new worker startup recorded | Still open |
| Preview | Owner identified; active runtime not yet inspected | Still open |
The two open rows are the point. “Mostly updated” can be a fair progress report. It is a poor completion report.
Provider responsibilities differ. As checked on 23 October, Vercel says projects on its Node.js 20 runtime must switch to 22 or 24 and redeploy. It manages patch versions for its 22 and 24 runtimes; those projects do not need to change their major-version setting. Its bulletin also tells operators of self-hosted Next.js and external Node backends to update their own runtimes. Vercel's security bulletin.
Do not apply that statement to a different host by analogy. Ask yours:
Which fixed Node.js build serves our production and preview deployments? Does the update cover existing instances, or must we redeploy? Are our workers and build environments included? Please provide the advisory and confirmation time.
That question is more useful than “are we secure?”, which tends to produce an answer shaped like a brochure.
A web application firewall may reduce exposure on traffic it inspects. It is not a universal runtime fix. Vercel explicitly notes that its WAF does not cover responses functions fetch from other servers. That distinction matters for URL importers and other outbound clients. Vercel's WAF scope.
As temporary containment, consider whether an affected input path can be disabled or restricted without breaking required behaviour. Verify where decompression happens before relying on a middleware switch. Record the limitation and an owner for the runtime update; do not let “temporary” become the longest-serving member of the engineering team.
Patching and incident investigation also answer different questions. A fixed runtime addresses the known flaw. It does not establish whether earlier exposure was exploited. Preserve relevant logs and use your incident-response process if you find suspicious activity. A crash alone is not a complete diagnosis, and an absence of crashes is not proof that nothing happened.
No. An affected runtime, a reachable input path and evidence of compromise are different things. Identify the first two promptly; do not invent the third. Framework names alone cannot establish your deployment's exposure.
Yes, if your deployed runtime is affected. Package auditing and runtime patch verification cover different layers. Keep both checks.
No. The names describe different research and vulnerabilities in different runtimes. The common operational lesson is to verify the software serving real workloads. The separate PHP2Shell guide covers PHP's fixes and hosting checks.
The current Node.js advisory credits OpenAI Codex Security, Khai Tran and Sylvie Mayer, for reporting the Brotli issue. Imperva describes its work as independent validation. Its attribution paragraph differs from the current Node.js page; this article follows the maintainer's current credit. Node.js advisory. An AI tool finding a real bug is the useful kind of AI: its output was checked and fixed by people, which is the line I draw in AI that ships, not AI that demos.
That is an architectural decision, not a substitute for this patch. Other runtimes may share native dependencies and need their own advisory checks. Choosing an API stack involves more than swapping a launch command during an incident.
Keep the fix boring and the evidence specific: a supported runtime, a completed rollout, working application paths, and a record someone else can verify. That is considerably more useful than winning the argument about which language had the worse week.
Cover: an AI-generated editorial illustration using the official Node.js logo as a visual reference. Node.js is a trademark of the OpenJS Foundation. This independent article is not endorsed by Node.js or the OpenJS Foundation.
Was this useful?
No account needed. One like per browser; click again to undo.
Prabir Singh
Full-stack engineer in Mumbai. Twenty years in sales taught me why people buy; seven years of code taught me how to build what they'll pay for. I still read error messages the way I once read objections: as the most honest feedback of the day. More about me or start a project.
A practical guide to PHP2Shell: the two critical PHP flaws, patched versions, hosting checks, WAF limits, and what to verify before calling the incident closed.
How I built prabir.dev for design and speed: 98 mobile and 100 desktop PageSpeed results, practical Next.js lessons, code examples and a launch checklist.
How I wired GA4, Google Tag Manager and self-hosted Umami with Consent Mode v2 in Next.js, plus the GTM consent bug that silently dropped every first visit.