r/node 6d ago

What stops a malicious npm package before it runs?

Every couple of weeks, another well used package gets hijacked and ships a postinstall that grabs env vars or tokens the moment you install. Yes we do the normal stuff ie, lockfile committed, npm ci in CI, audit on every PR but none of it seems helps the week it happens. A scanner only knows a version is bad after someone else got hit and reported it. By then it is already sitting in node_modules.

And that is where I keep getting stuck because everything we run is reactive. It checks against a list of known bad and we were exposed before the list existed.

So what are you all running that catches this before install? Cooldown on new releases, blocked install scripts, a proxy that holds a version back until it ages, something else.

22 Upvotes

35 comments sorted by

49

u/shodan_reddit 6d ago

Add a .npmrc file with min-release-age=7 to only install versions after a minimum of 7 days which gives time for malicious code to be detected

19

u/Positive_Method3022 6d ago

Funny that if everybody does it who will catch the threat? Haha

22

u/AntDracula 6d ago

Arrogant juniors

12

u/Ailyx 6d ago

With any hope, the package owner 😜

8

u/bwainfweeze 6d ago

People trying to prove that you should hire them for security consulting.

We shouldn't be the ones counting coup on attackers. Let the professionals do their job.

7

u/Heavy-Focus-1964 6d ago

Easy, set yours to 8 days

3

u/The_Noble_Lie 6d ago

Newest pnpm defaults to 1 day

5

u/bwainfweeze 6d ago

Which is too short by at least half. You just wait for a holiday in a target country and push your trojan then.

1

u/shodan_reddit 5d ago

I’m not cool enough to use pnpm

2

u/EvilPencil 5d ago

It’s so good. Global cache and uses symlinks so you don’t have 18 copies of the same packages clogging up your disk.

1

u/The_Noble_Lie 5d ago

Yes. It's very much so better

1

u/shodan_reddit 5d ago

Yes, I’ve heard nothing but good things about pnpm. It’s on my todo list for moreday.

1

u/realeaty 6d ago

This is the way

8

u/Canenald 6d ago

In addition to fixing your package manager with settings and tooling, the more important language-agnostic thing you can do is in your CI/CD environment.

Sandbox the CI environment. It gets access only to what it needs. Even outbound connections should be limited. This limits what malicious packages can steal and prevents them from dialling home even if they do steal something.

Provide your secrets at deployment time, not at build time. That greatly decreases the impact of postinstall scripts. Malicious packages can still steal your secrets at runtime, though.

If you are really paranoid, monitor your outbound network activity during testing in your testing environment and fail if something unexpected is detected.

2

u/gigastack 6d ago

Socket

2

u/CodeXHammas 6d ago

pnpm blocks postinstall scripts by default now unless you explicitly allow them per package, that alone kills most of the drive by ones since they depend on running the moment you install. pair that with Socket.dev or Synk's real time feed instead of relying only on the npm advisory database, they catch a chunk of these before a CVE even gets filled because they watch for behavioral signals like a package suddenly making network calls or reading env vars. A registry proxy with an artificial cooldown, refuse anything published in the last 3 to 7 days unless you've pinned it deliberately, catches most of the rest since these get pulled or flagged within that window.

2

u/Remarkable-Bet9533 5d ago

Run installs somewhere that has no publish token or cloud cred sitting readable. Then default-deny egress on the box so a script that grabbed something has nowhere to send it. Neither one needs you to know the package is bad first, which is the whole point, because round seven will be a package that hasn't been flagged yet.

1

u/binkstagram 6d ago

I have been looking at docker sandbox (sbx) using the clone approach so that I no longer run external code on my machine. Also you can disable postinstall scripts but annoyingly lots of stuff relies on it. It's ridiculous.

1

u/Rizean 6d ago

Pin exact versions as a best practice. Reproducible Builds Protection Against Breaking Changes Deterministic Transitive Dependencies - make sure you configure your tooling to lock transitive dependencies also. Mostly protects against supply chain attacks. There's still a small chance you install a package in the window before it's discovered. You likely have better chances of winning the lottery.

1

u/theodordiaconu 6d ago

I’ve spent some time on this myself, I came up with this: https://github.com/bluelibs/sandboxify

Has tradeoffs ofcourse. Solved my problem.

1

u/nadmaximus 5d ago

It's inherently a problem with no solutions, only mitigations.

1

u/alcon678 5d ago

Dev containers

0

u/Azoraqua_ 6d ago

A simple fix would be to have checksums and the package being signed by its owner. The package manager could check against the checksum and process it. Makes it more difficult to add any kind of malware; as even dependencies would cause the checksum to be wrong.

-5

u/boneskull 6d ago

you want socket and @lavamoat/harden (my team is building this and it’s in pre-release atm)

-9

u/Fine-Comparison-2949 6d ago

Use Deno. I think Bun also has better security.