How to avoid NPM supply chain attacks.

michael sorensen
ITNEXT
Published in
8 min readMar 19, 2022

--

Okay, so you have heard the news; The NPM supply chain is completely vulnerable to the whims of a few devs and their egos, “Protestware” is all the rage and the whole javascript ecosystem a big stack of cards waiting to come tumbling down at any moment.

Alright now that we’re on the same page let’s take a deep breath, grab a cup of coffee and talk about how we can easily protect ourselves while continuing to do our work and quietly ignore all the world problems surrounding us. (kidding)

Table of Contents

  1. Why is this a big deal now?
  2. How Supply Chain Attacks Work
  3. Best Practices / How to use Docker without knowing Docker (it’s easy now I promise)

Why is this a big deal now?

Honestly, these issues have been on the horizon for years. Developers who subscribe to other languages in other ecosystems will be quick to educate you on how bad javascript is because javascript developers will install just about anything to avoid writing code.

And well, as a javascript dev myself, I don’t think they’re wrong.

Recently and I mean, in the last 3 months we have had 2 high profile package authors publish malicious code to their code base and have potentially effected a large amount of end users.

Marak, the author of Faker.js and Color.js published an update that infinitely pasted algo text in the terminal preventing any dependent application from running. Seemingly to protest his lack of compensation for contributing to larger projects.

Brandon Nozaki (Aka RIAEvangelist), the author of node-ipc, approved a pull request into master that replaces all file contents on the system with a heart emoji. Effectively wiping everything. And while this “protestware” was targeting Russian and Belerusian computers, an american NGO was impacted by this attack.

This is not the first time malicious code has been published to NPM either. However, these have hit mainstream news sources because they are extremely popular and each get tens of millions of downloads every week. So how does this work and how can we protect ourselves?

How Supply Chain Attacks Work (for context)

Before going into the solutions it’s important that everyone reading this understands the problem. If you are already familiar with supply chain attacks I recommend skipping this portion.

And before you google “Supply Chain Software Attack” on google bear in mind that the answers you will likely find there are a little different than what we’re talking about today since most of those examples reference organizations purchasing code from other organizations. Since NPM is open source it’s important to keep in mind that all the players are usually individuals.

I think analogies can be confusing so let’s get straight to the nitty gritty. NPM stands for “Node Package Manager”. It is a command line tool. It is a package registry and it is a company (that was recently purchased by github).

NPM the company provides the NPM command line tool for free and the Node.js community has elected to include it when you install node. Users can submit code (packages) to the NPM registry and users can install packages from the registry by using the CLI tool.

Since you are reading this you are probably doing this all the time already so what’s the problem?

Well I am getting to that. like I said you can skip this part if you want.

Writing code is challenging and/or tedious so it is very common for users to download a package from NPM to save them the hassle of re-writing the code themselves and wasting all that time and energy. As a result the new code now depends on the aforementioned package to function properly. Now if the new code is bundled into a package any code written on top of that will now depend on 2 packages. Here’s a diagram because that sounded confusing even to me.

node_modules light

So what’s the problem? And the answer is the reader’s lack of patience. Stay with me now.

If the user writing myHelloWorldApp.js installs world.js they might not even know they also installed hello.js unless they thoroughly reviewed the world.js github page which is unlikely.

And now if the author of hello.js publishes an “update” to hello.js to NPM. And that “update” over-writes the user’s hard drive with emojis well that would be a supply chain attack. There we made it. Are you happy?

the author of hello.js updated their package to include malware. Now everything that depended on it is corrupted.

In Summary

A supply chain attack can occur anytime you are downloading or executing third party code on your machine. That’s why your OS is always asking you DO YOU TRUST THIS APPLICATION!?!? or some variation. All Open-Source Package Managers like NPM, PIP, etc are extremely vulnerable to these types attacks because anyone can upload anything to a trusted platform and it is up to the users to defend themselves. Luckily, the community does do a good job of policing itself and issues like these resolve themselves very quickly. Usually.

Best Practices

Okay, so now what?

Fortunately, there are a couple of relatively easy steps we can perform to not get burned like the aforementioned American NGO did when they installed node-ipc. I will list them all here and go into more detail below.

  1. trust no one not even your loved ones or the dog.
  2. Trace every ip connecting to your computer and destroy the whole thing if you notice an unfamiliar ip address.
  3. Take all the cash out of your bank and put it in your mattress
  4. Stock up on re-fried beans and hope for the best.

Okay, okay all jokes aside here’s the real list

  1. Pin your version numbers in package.json
  2. Use a package-lock.json and install with npm ci
  3. Use docker or a VM
  4. Common sense
  5. Use your own package servers
  6. Use a vetting service

To pin a version number in your package.json you just need to remove the ^ character that is automatically prepended to the version number when you install a package for the first time.

remove carrot to pin version number.

This will prevent NPM from installing the latest valid version and instead only download the version you know you can trust. For more information about the symbols in NPM packages go here: https://docs.npmjs.com/about-semantic-versioning (I also encourage you to check out the version calculator)

But what about my dependency’s dependencies? Well that’s where package-lock.jsoncomes in. This file tracks every single package that is being used for your application and the version being used. If you npm i or npm install your packages and something updates, the package-lock.json file will also be updated to reflect that change.

We don’t want that though. We want package-lock.json to yell at us if we are trying to install a package that does not match the version that is listed.

That’s where npm ci or npm clean-install comes in. It will Error if it tries to install something that conflicts with the package-lock.json file. For those of you who deal with package-lock.json merge conflicts all the time, installing packages with this command will also help mitigate some of that.

And Docker!

I know, I know. For a lot of people docker has been this sort of beast that’s mostly lived in the DevOps world. YAML files are kind of gross and difficult to debug when you have a problem.

But, the Docker team has done an amazing job trying to make utilizing a VM easier and easier every year and now it’s so easy you can setup a persistent development environment in a container (on windows no less!) in less than 5 minutes.

Before, we go through that 10 second tutorial let’s talk about why it’s important.

Pinning your version numbers and reviewing code is fine and all and still important but, part of fun of developing is the ability to explore the internet and try stuff out without fear. Why do we need to tip-toe around because some developers decided they wanted to go on a power trip?

The answer is we don’t. We can set up a safe space to download anything we want while isolating it from the things we actually care about and Docker (and VSCode) make achieving that easy. Here are the steps to add docker to an existing project:

  1. Add the Remote Containers VSCode Extension
  2. Navigate to your project directory
  3. Press Ctrl+Shift+P and type “add development container configuration files”

4. Select Node.js (if it’s a node project of course)

5. Press Ctrl+Shift+P and type “open folder in container”

6. Download and install docker if necessary

7. Done! VSCode should have re-opened itself with a remote connection to your new container with all of your code in it.

Now you can install whatever you want without worrying about bricking your hard drive.

Use your own package servers and vetting service

This was a suggestion provided by a u/Laladelic on Reddit. I am not very familiar with using either of these solutions but, the user did provide links to available solutions:

vetting service: https://snyk.io/

package servers: https://jfrog.com/artifactory/

In Conclusion

None of these solutions are foolproof. Misconfiguration or misunderstanding of the above solutions can defeat their purpose.

It is also up to all of us individually to decided what the right level of security is. How much risk we are willing to take.

There is no such thing as perfect security but, I make these suggestions because I love open source and would hate to see its reputation tarnished because a false sense of trust was attributed to strangers on the internet.

Be safe out there.

P.S. If you want to correct or add anything to what I have written please write a comment and I will do my best to update this post as fast as I can!

[Edit] updated to include package server and vetting service suggestions

--

--