DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

Postmortem: Our Next.js 15 App Was Hacked via a Third-Party npm Package

Postmortem: Our Next.js 15 App Was Hacked via a Third-Party npm Package

A detailed breakdown of how a compromised dependency led to a full application breach, and how we hardened our supply chain.

Incident Summary

On October 12, 2024, our team detected unauthorized access to our production Next.js 15 application, which powers our e-commerce checkout flow. The breach originated from a malicious update to a third-party npm package we used for legacy image optimization, next-legacy-image-optimizer (v2.1.4), which had been compromised via a maintainer phishing attack.

Timeline of Events

  • October 10, 2024, 14:22 UTC: The legitimate maintainer of next-legacy-image-optimizer fell for a phishing email disguised as an npm security alert, handing over their 2FA credentials.
  • October 10, 2024, 16:45 UTC: Attackers published a malicious v2.1.4 update to the package, injecting a cryptomining script and a backdoor that exfiltrated environment variables.
  • October 11, 2024, 09:15 UTC: Our CI/CD pipeline ran npm install as part of a routine nightly build, pulling the compromised package version without triggering any alerts (we had no dependency pinning for patch versions).
  • October 11, 2024, 10:30 UTC: The compromised build was deployed to production via our automated Vercel deployment pipeline.
  • October 12, 2024, 08:45 UTC: Our monitoring system flagged abnormal CPU usage on Vercel edge nodes, and Sentry caught unhandled errors from the injected script.
  • October 12, 2024, 09:20 UTC: We rolled back to the previous build (v1.2.3) and revoked all production environment variables as a precaution.

Root Cause Analysis

We identified three contributing factors to the breach:

  1. Unpinned Dependency Versions: We used caret (^) semver ranges for all third-party dependencies, allowing automatic patch updates. The malicious v2.1.4 was a valid patch update from v2.1.3, so it was pulled automatically.
  2. No Supply Chain Scanning: We did not run static analysis or supply chain security tools (e.g., Snyk, npm audit) in our CI/CD pipeline, so the malicious package passed all checks.
  3. Over-Privileged Environment Variables: Our production build process had access to all environment variables, including STRIPE_SECRET_KEY and JWT_PRIVATE_KEY, which the backdoor exfiltrated to a remote C2 server.

The malicious package code injected a postinstall script that added a middleware to our Next.js 15 App Router, which ran on every request. The middleware checked for admin routes, exfiltrated cookies and headers, and executed the cryptomining script during off-peak hours.

Impact

  • ~12 hours of abnormal cryptomining activity on Vercel edge nodes, leading to a $420 unexpected infrastructure bill.
  • Exfiltration of 14 production environment variables, including payment processor keys (no fraudulent transactions occurred, as we rotated keys immediately).
  • ~2 hours of downtime during rollback, affecting 1,200+ checkout sessions.
  • No customer data was accessed, as PII is stored in a separate, isolated database with no dependency access.

Remediation Steps

  1. Rolled back to the last known good build (v1.2.3) and pinned all dependencies to exact versions in package-lock.json, committing the lockfile to version control.
  2. Revoked all production environment variables, rotated secrets, and restricted build-time environment variable access to only required keys using Vercel's environment variable scoping.
  3. Integrated Snyk into our CI/CD pipeline to scan all dependencies for known vulnerabilities and malicious packages before deployment.
  4. Replaced next-legacy-image-optimizer with Next.js 15's built-in next/image component, removing the third-party dependency entirely.
  5. Conducted a full audit of all third-party packages, removing 7 unused dependencies and downgrading 3 others to maintainer-verified versions.

Lessons Learned

  • Never use caret (^) or tilde (~) semver ranges for dependencies in production applications. Always pin to exact versions and review all dependency updates manually.
  • Supply chain security is as important as application security. Run dependency scans in CI/CD, and use tools like Socket to detect suspicious package behavior (e.g., unexpected postinstall scripts, network requests).
  • Follow the principle of least privilege for environment variables: only expose what the build process absolutely needs, and never include production secrets in build-time variables if possible.
  • Next.js 15's App Router middleware is a high-value target for attackers. Audit all custom middleware and third-party packages that modify the request/response lifecycle.

Conclusion

This breach was a wake-up call for our team to prioritize supply chain security. While the impact was limited, it highlighted gaps in our dependency management and CI/CD processes. We've since implemented all remediation steps, and no further malicious activity has been detected. We hope sharing this postmortem helps other Next.js teams avoid similar pitfalls.

Top comments (0)