Keep malicious Node modules behind bars!
Tags: security, node, javascript
Published: 2021-04-12 21:19:18

---
title: Keep malicious Node modules behind bars!
published: true
description: How to ensure the Node modules you install, don't steal your data
tags: security, Node, JavaScript
cover_image: https://cdn.arstechnica.net/wp-content/uploads/2021/03/software-code-800x534.jpg
---

When you use a Node module, have you thought that maybe a dependency of a dependency that your script uses, could [become malicious](https://www.npmjs.com/advisories/1584)?

That dependency has the same access to your computer as your own code. It could look through your files then phone home anything it wants, as recently seen with crypto wallets in the [event-stream attack](https://www.zdnet.com/article/hacker-backdoors-popular-javascript-library-to-steal-bitcoin-funds/), or [sensitive Linux files](https://arstechnica.com/gadgets/2021/03/more-top-tier-companies-targeted-by-new-type-of-potentially-serious-attack/). These types of attacks are called "open source supply chain attacks", and hundreds of malicious package attacks have been [documented](https://link.springer.com/chapter/10.1007/978-3-030-52683-2_2).

Deno [forbids access to the filesystem by default](https://deno.land/manual/getting_started/permissions), and you can whitelist specific directories. Node doesn't have this capability.

One solution is to run your scripts in a sandboxed environment such as [firejail](https://github.com/netblue30/firejail/), [bubblewrap](https://github.com/containers/bubblewrap), or a VM (Docker is a containerization solution, [not aimed at security](https://security.stackexchange.com/questions/107850/docker-as-a-sandbox-for-untrusted-code)).

Once you've installed and configured sandboxing, it would help to check if your scripts are indeed sandboxed correctly, and abort immediately otherwise, so that no dependency has a chance to execute. To that purpose, I've published a module called "**[behind-bars](https://www.npmjs.com/package/behind-bars)**", which does exactly that. All you have to do is add this line at the top of your script:

```js
import 'behind-bars';
```

The module will call `process.exit()` immediately if it can access sensitive files or directories (browser profiles, cryptocurrency wallets, ~/*_history etc). It does so before any other imported module has a chance to run and steal data, by only using synchronous calls. You can optionally configure it to also ensure there's no Internet access, and you can define your own custom paths to sensitive files.

So far it checks for common sensitive files on Linux and MacOS systems. PRs for Windows are welcome in [the repo](https://github.com/dandv/behind-bars)!


## Implementation challenges

To provide the maximum level of security, the module code needs to execute before any other imported code. Thus the developer must add the `import 'behind-bars'` line *first* at the top of the script. At that point, the module code needs to complete two types of requests before any other code runs:

1. Checking the filesystem for accessible paths
2. Checking for Internet access (by fetching a specified URL)

Both these requests are normally asynchronous, which means other module code will get executed before they complete. This is unacceptable. Starting with Node v14.3.0, top-level `await` has been unflagged, and we could use it to wait for the requests to complete. The problem is, when transpiling to CommonJS, top-level await doesn't actually block the other imports from executing.

This leaves using synchronous requests as the only backwards-compatible option. Unfortunately this means using old libraries like `glob` and `sync-requests`, which pull a total of ~40+ dependencies. If dropping CommonJS compatibility were ok, we could use the far lighter [tiny-glob](https://www.npmjs.com/package/tiny-glob), and node's native `http`/`https` module.