What’s the worst that could happen after npm install
?
When you open an app or execute a program from the terminal, that program can do anything that you can do.
In a nutshell: Imagine if your computer were to disappear in front of your eyes and re-appear in front of mine. Still open. Still unlocked. What could I do from this moment on? That is what an unknown program could do.
Upon running npm install
, you may be downloading and executing hundreds of programs.
Programs from nice people sometimes ask for your permission. This is because a developer choose to do so.
There may also be laws that could punish them if they get caught not doing so.
What about programs of which the authors choose differently? Well, such program could do quite a bit.
- It could access any of your files, modify them, delete them, or upload them. This also applies to the internal files used by other applications.
- It could install other programs in the background.
- It could talk to other devices linked to your home network.
What is at stake
Files you might not be thinking about:
- The cookies in your web browser.
- Desktop applications. Chat history, password managers, todo lists, etc. They all use files to store the text and media you send or receive.
- Digital media. Your photo albums, home videos, and voice memos.
- SSH private keys, GPG key rings, and other crypto files used by developers.
Browser cookies
Browsers cookies make it so you’re immediately logged-in when you open a new tab for Gmail, or Twitter. An evil program can copy the browser’s cookies file and share it with the attacker.
They could then read any e-mail you’ve ever received or sent stored there. It could also delete any. (Got a backup?) They can naturally access future e-mails as well. Like the ones you get from “Forgot password” buttons. They could also hide any trace of these (e.g. filter rules).
This affects any website you use. Social network? Access to any post or DM — regardless of privacy setting. Company e-mail, Google Drive? That too.
Sleeper programs
The evil program may configure itself to always start in the background when you open your laptop. A new friend for life!
It could also add local command-line programs that wrap the popular sudo
and ssh
commands, to make them do a little extra behind the scenes. Next time you run sudo <something>
to perform an administrator action and enter your password—you may have given away full system access. Deploying some code? Running ssh cloud.someplace.special
might let the attacker tailgate along with you, opening one shell for itself and another for you.
Local web server
These background programs could also affect you in a myriad of other ways. I won’t detail those today, except to mention they can keep a local web server running. Spotify and Zoom have been seen in the news doing questionable things with their local web servers.
Is this an npm problem?
Maybe. Technically these concerns apply to any method of executing unknown code. Running npm install
isn’t very different from pasting a command like curl url… | bash
. They both execute a downloaded program from your terminal. The difference is in user expectation.
Upon seeing the url and the bash
invocation, you have a choice: Trust the publisher (the url), or trust the script (download, review, then decide whether to run). The result is generally predictable and without hidden dependencies.
Other package managers
What about Debian (apt-get) or Homebrew? Like npm, code published there is unknown to most of us and hard to review. But, there is an important difference: Peer-review. These traditional repositories are curated by a central authority. You don’t have to trust the script or original authors of each package, so long as you trust the publishers and their curation process.
The scale has changed the game
What about PyPI or Packagist (Composer)? These are like npm. Anyone can publish anything. There is however a difference in scale. PyPI has 194K projects. Packagist is host to 237K packages with 0.5 billion downloads a month. npm has over 1.3 million packages and 30 billion downloads a month. This makes it a much more popular target. [1] [2] [3]
Dependency graphs
There is also a difference in habit: PyPI packages have 7 dependencies on average, with typically 1 indirect dependency. And, I would expect most dependencies there to be from authors the user has trusted before. [4] Snyk.io published in April that the average npm package has a whopping 86 dependencies, with a 4+ levels of indirect dependencies. [4]
The ESLint package has 118 npm dependencies [5]. Eleventy, a popular static site generator, requires 555 dependencies (Explore dependency graph). Each one of these may run arbitrary shell commands from the terminal both during the installation process, after later when using the tool.
I get it. Now, what can we do about it?
There isn’t a magic bullet to make everything perfectly safe. But, there are a number of things you can do to reduce risk.
Isolation
For the past year, I’ve been using disposable Docker containers as a way to reduce the risk of compromise. It has controls for network access, and for which directories can be exposed. Docker isn’t a perfect safety net by any means, but it’s a step in the right direction.
My base image uses Debian and comes with Node.js, npm, and a few other utilities (such as headless browsers, for automated tests). I use a bash script to launch a temporary container, based on that image. It runs as the unprivileged nobody
user, and mounts only the current working directory.
From there, I would run npm install
and such. The only thing it interacts with is the source code and local node_modules
directory for that specific project. It isn’t given access to any other Git repos, desktop apps, browser cookies, or crypto files. And, once that terminal tab is closed, the container is destroyed.
I’ve published the script I use at github.com/wikimedia/fresh. I don’t recommend using it outside Wikimedia, however. Create your own instead. The repository explains how it works.
Other options for isolating your environment:
- Speed and flexibility: Use
systemd-nspawn
orchroot
. This takes more work to setup, but provides a faster environment than Docker. In terms of security it is comparable to Docker. Read more systemd-nspan on ArchWiki. - Security and ease of use: Use a virtual machine (e.g. VirtualBox/Vagrant). This is more secure by default and offers a GUI for controlling what to expose. The downside is that VMs are significantly slower.
Fewer dependencies
Finally, you can reduce risk by reducing the number of packages you depend on in your projects (and then shrink-wrap them). Especially development dependencies, as these tend to be explicitly aimed at executing from the CLI.
Question yourself and question others before introducing new dependencies. Perhaps even encourage maintainers of your favourite packages to Reduce the size of their dependency graph!
Further reading
- Deconstructing Spotify’s local server by Carl Byström (2013).
- Detect piped curl on the server-side, Phil at idontplaydarts.com (2016).
- Malicious code on npm, Danny Grander, Snyk.io (2018).
- npm Install Hook Scripts, Alex Wendland (2018).
- When packages go bad, Jake Archibald (2018).
- I’m harvesting passwords from your site, David Gilbertson (2018).
- An idea for improving npm package permissions, David Gilbertson (2018).
- Shell vulnerability in iTerm 2, Tom Ritter, Mozilla Security (2019).
- Strange things after installing an NPM package, Vladimir Tikhonov (2019).
- Apple removes Zoom’s server, John E Dunn, Naked Security (2019).
- Zoom 0-day: 4+ Million Webcams, Jonathan Leitschuh (2019).
Update (6 Aug 2020): Check out Worrying about the NPM ecosystem, which takes a more scientific look at the problem.
Update (2 September 2023): Further reading I’ve accumulated on this topic:
- On cost of platform dependencies and services, Tim Starling (2018).
- Worrying about the NPM ecosystem, Sam Bleckley (2020).
- npm audit: Broken by design, Dan Abramov (2021).
- Check-in your node dependencies, Jack Franklin (2021).
- Coa npm package got malicious code, sizeof.cat (2021).
- Inside Your node_modules Folder, Feross Aboukhadijeh (2022).
- CTX package on PyPI has been hacked, Reddit (2022).
- We’re drowning in software dependencies, Ryan Barrett (2022).
- “Out of the Software Crisis”: Dependencies, Jim Nielsen (2023).