npm Security Best Practices
The npm ecosystem is the largest package registry in the world, and that scale makes it a high-value target. Attacks like Shai-Hulud, event-stream, and eslint-scope have all demonstrated the same uncomfortable truth: installing a package can execute arbitrary code on your machine before you’ve read a single line of its source. These npm security best practices help you reduce that risk at every stage—installing, developing, and publishing.
Key Takeaways
- Disable post-install scripts globally and allowlist only the packages that genuinely need them.
- Set a release age cooldown so brand-new (and potentially malicious) versions never reach your builds automatically.
- Always commit your lockfile and use
npm ciin CI pipelines for reproducible, tamper-resistant installs. - Protect maintainer accounts with WebAuthn-based 2FA and publish via OIDC trusted publishing to eliminate long-lived tokens.
Why npm Supply Chain Security Matters Now
Supply chain attacks don’t exploit your code. They exploit your trust in someone else’s. When a compromised package lands in your node_modules, it can steal credentials, exfiltrate environment variables, or propagate itself further. The Shai-Hulud worm alone triggered the removal of over 500 packages from the registry in a single incident. Vulnerability scanning alone won’t catch this class of attack—you need defense in depth.
Secure Dependency Management: Harden Your Installs
Disable Post-Install Scripts
Post-install scripts are the most common attack vector in npm supply chain attacks. Disable them globally:
npm config set ignore-scripts true
npm config set allow-git none # npm CLI 11.10.0+
The allow-git=none setting matters because a git-based dependency can ship its own .npmrc that silently re-enables lifecycle scripts, bypassing ignore-scripts entirely.
If you use pnpm, version 10+ disables post-install scripts by default. Use pnpm-workspace.yaml to explicitly allowlist packages that legitimately need them:
allowBuilds:
esbuild: true
core-js: false
Enable strictDepBuilds: true to turn any unreviewed build script into a CI-blocking failure rather than a silent warning. Bun also blocks post-install scripts by default, with opt-in via trustedDependencies in package.json.
When you do need lifecycle scripts, use @lavamoat/allow-scripts to create an auditable allowlist rather than enabling scripts globally.
Add a Release Age Cooldown
Malicious packages are often discovered and unpublished within hours or days. Skipping brand-new versions gives the community time to catch problems before they reach you:
npm config set min-release-age 3
This tells npm to ignore any package version published less than three days ago. For automated dependency updates, tools like Renovate and Dependabot support equivalent minimumReleaseAge settings.
Commit Your Lockfile and Use npm ci
Always commit package-lock.json. In CI environments, replace npm install with:
npm ci
npm ci installs exclusively from the lockfile, fails if there’s any mismatch, and never silently resolves a different version. This is the foundation of reproducible, secure builds in automated pipelines.
Don’t Upgrade Blindly
npm audit and npm outdated are useful signals, but treat them as one input, not a complete solution. Before upgrading any dependency, review the changelog. Avoid bulk upgrades that skip this step—each version bump is a potential attack surface change.
Discover how at OpenReplay.com.
Secure npm Workflows for Package Maintainers
Enable 2FA—and Upgrade to WebAuthn
Account takeovers are the primary mechanism behind supply chain attacks on the registry. Enable 2FA on your npm account with write-mode protection:
npm profile enable-2fa auth-and-writes
GitHub increasingly encourages passkeys and WebAuthn-based authentication, which are significantly more phishing-resistant than traditional TOTP-based 2FA. A hardware key or passkey is significantly harder to phish than a TOTP code, so switching sooner rather than later is worthwhile.
Publish with Trusted Publishing (OIDC)
Long-lived npm tokens are a liability. Trusted publishing via OIDC lets GitHub Actions or GitLab CI authenticate directly with npm using short-lived, workflow-scoped credentials—no stored token required. It also automatically generates provenance attestations, giving consumers cryptographic proof of where and how a package was built.
This is the direction the ecosystem is heading. GitHub has signaled plans to phase out legacy tokens and make trusted publishing the default path for automation.
Verify What You Install
Don’t assume the official registry is safe. Use Socket or npq to screen packages for suspicious behavior before installation. Check download counts, repository activity, and maintainer history—especially for packages suggested by AI coding assistants, which can hallucinate package names that attackers then register (a technique called slopsquatting).
Conclusion
No single tool prevents every npm supply chain attack. The practices that matter most are layered: disable lifecycle scripts, enforce lockfiles, add a cooldown on new versions, use npm ci in CI, and move publishing to OIDC with 2FA. Each layer independently reduces risk. Together, they make your workflow meaningfully harder to compromise.
FAQs
Yes, some packages like esbuild or sharp require post-install scripts to download platform-specific binaries. Rather than re-enabling scripts globally, use an allowlist approach with pnpm's allowBuilds configuration or LavaMoat's allow-scripts to grant permission only to specific, trusted packages that genuinely need build steps.
No. npm audit checks for known vulnerabilities in published advisories, but it cannot detect novel supply chain attacks like typosquatting, account takeovers, or malicious code injected into otherwise legitimate packages. Treat audit as one useful signal within a broader layered defense strategy that includes lockfile enforcement, script restrictions, and pre-install screening tools.
npm install reads package.json, resolves dependency ranges, and can modify the lockfile. npm ci reads only from package-lock.json, installs exact versions, and fails immediately if the lockfile is missing or inconsistent with package.json. Always use npm ci in continuous integration pipelines to guarantee reproducible and tamper-resistant builds.
Traditional npm tokens are long-lived secrets stored in CI environments, making them attractive theft targets. OIDC trusted publishing generates short-lived, workflow-scoped credentials tied to a specific repository and action run. No secret is stored anywhere, and each publish operation is cryptographically linked to its source, producing verifiable provenance attestations automatically.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.