Npm attacks have come into focus over the past several weeks; recent hits like
Once a developer’s credentials were compromised, the malware attempted to spread automatically to other packages under their account. This creates a massive attack surface that must be considered as we continue to see malware being pushed to npm and PyPI.
Why npm keeps getting targeted
Npm’s popularity makes it a prime target for cybercriminals: millions of developers depend on it daily. Npm and PyPI have infamous installation hooks, which get automatically executed once a package is installed. This makes it very trivial for attackers to actually execute malicious payload compared to other ecosystems.
As we’ve seen from these recent attacks, the blast radius potential is massive. Even a single compromised package is able to spread malware through various organizations across the software supply chain. As noted in the paper “
One of the main characteristics of the npm ecosystem is the high number of transitive dependencies. For example, when using the core of the popular Spring web framework in Java, a developer transitively depends on ten other packages. In contrast, the Express.js web framework transitively depends on
How npm plays out
These events follow a similar pattern. According to the
- Develop and Advertise Distinct Malicious Package from Scratch: This vector stems from the creation of a new OSS project, with the intention to use it for spreading malicious code from the beginning or at a later point in time.
- Create Name Confusion with Legitimate Package: This vector typically consists of creating project or artifact names that resemble legitimate ones, suggest trustworthy authors, or play with common naming patterns.
- Subvert Legitimate Package: When attacks aim to corrupt an existing, legitimate project, which requires compromising one or more of its numerous resources.
Once installation hooks are executed, malicious code runs automatically on every developer machine or CI system that pulls the package. While auditing every version is technically possible, doing so at scale is very hard in practice.
Remedying the Issue
What’s clear is that centralized gatekeeping is not a practical solution. We can’t have one party dictate that something is good or secure enough to be published and distributed. Instead, we need to make it easier for maintainers to follow best practices and for consumers to verify security.
These attacks have spurred conversations emphasizing the need of 2FA , attestations, and signing, which are key to protecting maintainer accounts and tracking artifacts’ provenance. What’s also important, however, is to make such features usable without putting additional effort on maintainers’ shoulders.
Usable security is important, however, ease-of-use and security are often at odds, especially when it comes to shortening token lifetimes and MFA. Several measures
- Shorter-lived granular access tokens: All newly created write-enabled granular access tokens now default to a 7-day expiration (down from 30 days), with a 90-day maximum. Shorter token lifetimes significantly shrink the window in which compromised credentials can be exploited.
- Migration from TOTP to FIDO-based 2FA: Npm is deprecating time-based one-time password (TOTP) 2FA in favor of stronger FIDO-based authentication.
- Deprecation of legacy classic tokens: Older token formats are being phased out in favor of more secure, scoped alternatives.
- Publishing-specific token restrictions: Granular tokens with publishing permissions will be limited to shorter expiration periods, and publishing access will default to disallow token usage—encouraging trusted publishers or 2FA-enforced local publishing.
- Removal of 2FA bypass options: Developers will no longer be able to bypass two-factor authentication when publishing packages locally.
- Expanded support for trusted publishing: Npm is increasing the number of eligible providers that can serve as trusted publishing sources.
The bottom line is that if you’re writing JavaScript every day, you are on the front line of npm security. Developers need to work closely with organization leaders to create guardrails against malicious code. There are a few best practices to keep in mind, one being cooldown periods, which are supported by more and more solutions, e.g.,
It’s also important to reduce the number of dependencies used. In practice, that means choosing a single library for each use case instead of pulling in multiple tools that do the same thing, and removing dependencies that are no longer needed after refactoring. Technologies like reachability analysis/call graph analysis are helpful to identify unused software dependencies in your packages.
Lastly, being attentive and watching for red flags, specifically being wary if a small package suddenly ships a huge change, adds an install script, or changes maintainers without explanation. While critical, this step is increasingly difficult to manage at scale without proper tool support. This is exactly what is difficult to do at scale -- unless there's proper tool support. Modern AppSec solutions should provide these health indicators for all the application dependencies to ensure proper monitoring can be executed at scale.
These attacks should serve as a lesson: it’s not if your team will face risk, but when. That’s why it’s so critical for the entire open source ecosystem - from OSS maintainers and OSS consumers to third parties like registries to all apply best practices. Ultimately, focusing on reducing the probability of the compromise and minimizing its reach is the most important thing developers and security leaders can do . Understanding how these recent npm attacks unfolded will help signal a way forward for a more secure software supply chain.