Prudent publishing to npm
Tags: npm, javascript
Published: 2019-08-21 17:43:23

This post shares a few practices I find helpful for keeping my publishing of packages to npm a bit more isolated from my personal environment. I think there are good principles here, but they may be a bit specific to my project and platform. Please do comment with variants or tips for your own environment. Hey - maybe even do a whole post of your own!

# 💌 Sign up to npm with a separate email

This is good practice for most developer channels - whether it's publishing an Android app in the Play Store, your account for a particular API you rely on, or your npm account. There are a number of reasons for doing this:

- ⛔ Accounts get banned by mistake! You as a person on npm is different to the account that owns your packages. If one gets banned, it doesn't affect the other.
- 🤝 Other people may need access. While an [organization](https://docs.npmjs.com/orgs/) is the right solution long-term, sometimes there are emergencies or one-offs and sharing access to the one specific account is definitely better than your personal one!
- 👔 This email may need to be public. You may want to provide support or perhaps you just get random enquiries. Using a separate address lets you manage that as it's own thing.

No real instructions on this one... source that email address wherever you want!

# 🎭 Publish using a separate local user

This has more explicit security benefits than using a separate email (which frankly is more about your personal happiness).

Most of the issues here come from other packages getting compromised and injecting malicious code. Two specific incidents here were with some of the [ESLint packages including code that stole npm tokens](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) and the [`event-stream` package getting compromised to steal cryptocurrency](https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident). Creating an isolated user helps mitigate this by reducing the exposure. So, for example if there is an exploited package that's able to kick in during the deployment process - it won't have access to your personal user. Likewise, an exploited package in your development environment won't have access to your deployment process.

If you're being really conscientious, then you should really ensure *all* your npm operations run as a separate user but that does start to turn into a lot of overhead.

I'm a Linux user, so these instructions are all based on my Bash shell environment. Please do share if there are tweaks for your own platform!

🐣 Right, let's get our user created:

```sh
$ sudo adduser deploy-npm
```

You'll be prompted to create a password. Obviously use something solid, but we don't need to care what it is. We're going to use `sudo` when we access it.

🕵️ Now we need to configure the environment for the new `deploy-npm` user. We'll use `sudo` to log in to that account:

```sh
$ sudo -i -u deploy-npm
```

The user will need to have access to a version of Node and `npm`. I use `nvm` to manage this, so the first thing I'm doing is following their [install instructions and getting the LTS version of Node](https://github.com/nvm-sh/nvm#installation-and-update). You can go about this however you want. For example, maybe you already have Node installed system-wide and it's therefore immediately available for the new user.

☑️ Verify your set-up just by checking the `npm` version:

```sh
$ npm -v
```

👋 Now it's time to log in to the account! Unsurprisingly, that done with:

```sh
$ npm login
```

🔐 If you haven't already enabled two-factor authentication on your account this is an ideal time to do it! This means even if your account is compromised or the tokens stolen the attacker still can't do anything without that second authentication mechanism.


```sh
$ npm profile enable-2fa auth-and-writes
```

💾 We also need a local copy of the repo. Our deploy user isn't going to commit anything and doesn't have SSH credentials set up anyway, so we're just going to use the HTTP version. This assumes your code is on GitHub, but any read-only checkout is what you're aiming for here.

```sh
$ git clone https://github.com/someUser/my-package.git
```

🔏 The assumption here is that you've already appropriately tagged the repo ready for a release. So, let's change into the directory and checkout the tag.

```sh
$ cd my-package
$ git checkout tags/0.0.1
```

🎁 That's all the set-up done, so now we publish! Assuming you did enable 2FA, you're going to get asked for your confirmation code here.

```sh
$ npm-publish
```

For my use, I've wrapped that up in a script that I can use to publish a given tag. This first one is running as the `deploy-npm` user and is effectively just the commands we used above. I've saved it as `deploy-npm.sh` in the user's home directory.

```sh
#! /bin/bash
cd &&
source .nvm/nvm.sh &&
cd my-package &&
git fetch &&
git checkout tags/$1 &&
npm publish
```

Finally, I have a script in my own account that does the appropriate `sudo` call to run as the `deploy-npm` user.

```sh
#! /bin/bash
sudo -H -s -u deploy-npm /home/deploy-npm/deploy-npm.sh $1
```

🤖 Now, after I've tagged up a release I just run:

```sh
$ deploy-npm.sh 0.0.2
```

If you're still looking to tighten things up, try this post from Liran with a selection of other useful tips. 
{% link https://dev.to/lirantal/npm-security-tips-to-keep-you-safe-of-malicious-modules-25bp %}