← Back to Blog

Node.js, PHP, and npm all had a bad October. Here is what it means for Shopify apps

8 min read

TL;DR

On 21 October both Node.js and PHP shipped critical security fixes, including remote code execution bugs, while npm saw a run of compromised packages. Shopify's own platform is not your server, but the apps and integrations merchants install run on exactly these stacks.

Node.js, PHP, and npm all had a bad October. Here is what it means for Shopify apps

October has been a rough month for the two runtimes most Shopify apps sit on.

On 21 October, Node.js and PHP both shipped security releases on the same day. Both included bugs that can end in remote code execution. Around them, npm had a run of compromised packages that stole developer credentials on install. And the day before, Shopify published Securing your app, together, flagged action required, asking every app developer to act that week.

None of this means a Shopify store was breached. It does mean the code around the store, the apps and integrations merchants trust with their data, needs patching now.

Who is actually exposed

A merchant on hosted Shopify does not run Node.js or PHP. Shopify runs the storefront, checkout, and admin, and patching that infrastructure is Shopify's job.

The exposure sits one step out, in everything a merchant connects:

  • Public and custom apps, which run on the developer's own servers, and very often on Node.js.
  • Apps built in PHP, including Laravel apps and anything using Shopify's newer official PHP package.
  • Headless storefronts and middleware that a team hosts itself rather than on Oxygen.
  • ERP, PIM, and fulfilment connectors, many of them PHP services that have not been touched in years.
  • The WordPress or WooCommerce site that still runs the blog or the wholesale portal next to the Shopify store.

Each of those holds a Shopify access token. An attacker who gets code running on one of those servers does not need to break Shopify. They use the token the app already has, with whatever scopes the merchant approved.

Node.js: decompression is the headline

Node.js fixed ten vulnerabilities across the 22, 24, and 26 release lines. The one to care about is CVE-2026-111497, an out-of-bounds write in the bundled Brotli decoder, scored 9.2 Critical.

Malformed Brotli data passed through node:zlib can corrupt process memory. According to Konfirmity's write-up, researchers then demonstrated code execution through two very ordinary paths: a Brotli request body handled by Express and body-parser, and a Brotli response read by the built-in fetch(), which asks HTTPS servers for Brotli by default.

That second path is the uncomfortable one for app developers. A Shopify app makes outbound calls all day: to its own APIs, to analytics and email providers, to ad platforms, to whatever URL a merchant pasted into a settings field. If any of those servers is malicious or compromised, a crafted response is enough. You do not have to be accepting anything unusual.

The other high-severity fixes are narrower but relevant to apps that connect to merchant-supplied addresses. Hostnames containing a NUL byte can be truncated by dns.resolve*() after your code has already validated them against an allowlist (CVE-2026-105482). TLS hostname checks can be fooled by non-canonical IP addresses such as 0127.0.0.1 (CVE-2026-106069). If your app validates a webhook destination or a link preview URL as a string and then connects to it, read those two closely.

The fixed versions are 22.23.4, 24.22.1, and 26.12.1. Node.js 20 and older get no fix. If your app still deploys on Node 20 because the template did, this release is the reason to move.

PHP: php2shell runs before your code does

PHP's two bugs are worse in one specific way. CVE-2026-109618 and CVE-2026-109619, nicknamed php2shell, allow unauthenticated remote code execution, and on Apache with mod_php the first one can be triggered before any application code runs.

Both sit in parts of PHP that almost every request touches. The first corrupts memory when a request hits memory_limit at the wrong moment, for example while parsing a form upload. The second leaves internal state pointing at memory from a previous request after a fatal error. Your framework, your authentication middleware, and your HMAC check never get a vote.

The fixed versions are 8.5.12, 8.4.27, 8.3.36, and 8.2.35. PHP 8.1 and earlier get no upstream fix. CISA's advice for those is blunt: take them offline until they can be upgraded. CISA had not seen successful exploitation as of publication, but a bug in request handling with a public advisory does not stay theoretical for long.

This is where merchants should worry most, because PHP is so often invisible. The connector that syncs orders to the accounting system, the reviews widget's backend, the agency-built middleware from 2021. Nobody thinks of it as "running PHP" until the advisory lands.

npm: the attack that comes in through your own tooling

The runtime bugs need someone to send you bad input. October's npm incidents did not. They ran on npm install.

In early October, tensorlake@0.5.144 and @subql/common@5.8.3 were both published with malicious install hooks that stole GitHub and npm tokens, cloud keys, SSH keys, and .env files. Tensorlake's payload also tried to republish itself into the victim's own packages, wrote .claude/settings.json and .vscode/tasks.json into repositories so it would run again when the project was opened in an editor, and threatened to wipe the home directory if the stolen GitHub token was revoked.

Two details matter for anyone shipping a Shopify app. Both packages came out of the projects' own release workflows, so the compromised versions carried legitimate-looking provenance. Provenance tells you where a package was built. It does not tell you the code is safe. And the target was the developer's machine, which is exactly where a Shopify app's Partner credentials, deploy keys, and production .env live.

A dependency-confusion cluster in the same weeks used inflated versions like 999.0.x to beat internal package names in resolution. If your app pulls private packages without a scoped registry, a typo or a misconfigured .npmrc is enough.

What Shopify asked developers to do

Shopify's 20 October post is unusually direct. It says attacks across the app ecosystem are increasing as AI gets better at finding vulnerabilities, and asks developers to act that week. The main asks:

  • Run the new shopify app security check, released on 15 October, which looks for unauthenticated routes, cross-shop data access, unverified webhooks, and committed secrets.
  • Move to expiring offline access tokens, which will soon be required for all public apps.
  • Treat rendered data, including metafields, as untrusted and escape it.
  • Check API request logs in the Dev Dashboard for suspicious sources, filtered by client IP.
  • Turn on two-step authentication everywhere that touches the app: Partner account, hosting, CDN, repositories, and email.

Shopify also says it is tightening storefront app code boundaries, adding IP restrictions so keys only work from your servers, and raising App Store security requirements. That last one is worth reading as a warning. The patching that is optional this week becomes a review requirement later.

What we did across our own apps

For Waitlist Flow, Shiftify, True Profit Analytics, and Rollback Hero, the response was mostly unglamorous:

  1. Moved every app to Node.js 22.23.4 or later, pinned in engines, the Docker base image, and CI, not only on a laptop.
  2. Added shopify app security check to pull requests in GitHub Actions, so the check runs on every release instead of once before submission.
  3. Confirmed all four apps use expiring offline tokens, and fixed one where the setting only applied to new installs.
  4. Started grouping related Dependabot updates so security bumps land together instead of sitting in a pile of unrelated pull requests.

None of that is clever. The useful part is that it is now automatic, which is the only way it keeps happening.

A short checklist

If you build Shopify apps:

  • Check the runtime your host actually uses: node -v in production, not your laptop.
  • Upgrade to Node.js 22.23.4, 24.22.1, or 26.12.1, and rebuild container images and serverless functions that pin an older version.
  • If you depend on the undici package directly, upgrade it too. Upgrading Node.js does not change it.
  • On PHP, upgrade to 8.2.35 or later on a supported branch. If you are on 8.1 or older, migrate.
  • Install with lockfiles and npm ci, disable install scripts where you can, and use a scoped registry for private packages.
  • Rotate tokens if you installed any of the affected npm versions, but follow the incident guidance first. With Tensorlake, revoking before removing the payload was the trigger.
  • Run shopify app security check before every release.

If you are a merchant:

  • Open your installed apps list and remove anything you no longer use. An unused app with read and write scopes is pure risk.
  • Ask the developers of custom apps and integrations, in writing, which runtime versions they are on and whether they have patched. A good partner will answer in a sentence.
  • Do the same for any PHP connector or WordPress site connected to your store.
  • Turn on two-step authentication for every staff account and remove staff who have left.

The point of all this

Shopify has spent the last year making it easier for merchants to install apps and connect tools. That is good for merchants. It also means the security of a store is now the security of every server it trusts with a token.

October was not a breach. It was a reminder of where the weak points are, delivered on the same day by two runtime teams, a run of npm attackers, and Shopify itself. Patch the runtimes, automate the checks, and remove what you do not need.

If you want a second pair of eyes on an app or integration before Shopify raises the bar, book a consultation.