Javascript security

Npm security and license compliance best practices

Javascript pitfalls and how to avoid them

Niclas Gustafsson
ITNEXT
Published in
5 min readFeb 2, 2021

--

Build it better and safer, using these 10 best practices

In this post we’ll look at some best practises that my team and I believe in when building for the JavaScript ecosphere, whether you’re in a fast growing start-up or a larger organisation. We will look at how to best avoid our top-10 potential pitfalls so that you can build with confidence on the success of others!

Vulnerabilities, Licenses, Dependencies…

Nowadays dependency management, as well as security and license compliance, are all issues that need to be a priority for any team or organization.

Photo by Procreator UX Design Studio on Unsplash

With almost 1.5 million packages available in the public npm registry and up to 90% of the code in modern applications is open source code developed by others.

So what does this actually tell us, besides the fact that we love having the opportunity to just pick what we need, install it and move on?

Below we’ve compiled a checklist of what we believe in regarding best practices. Further down there is a bit more information about each of the top 10 best practices.

TL;DR

There are many ways to secure your code supply chain. We at Bytesafe have compiled a PDF with the top 10 security best practices that we think all developers and companies should think about. So if you’re in a hurry or want a one-pager of the 10 npm security best practices, then this quick guide is for you.

Click to download printer-friendly version (PDF in article)

10 npm best practices

1. Benefit from extra security using a private registry

Using a private registry, like Bytesafe, adds a central hub for all your packages where you can add controls and rules.

Instead of depending on the publich registry where team members can download and install any package, you add extra security by curating your private registries, see all package dependencies, allow for packages to be cached and most important: scanning for security and license issues.

2. Scan your packages for security issues

The npm team has made great efforts to improve security together with the community as a whole, but new malicious packages are continuously detected and added to advisory databases.

Most often it takes time until a package with securitu issues has been detected and the dependency has been removed from your application. Therefore it’s very important to continuously scan for security issues so that you are notified as soon as possible.

Don’t just rely on triggered scans (like npm audit) during installation.

3. Stay on top with your open source licenses

Using a package with the wrong license could have catastrophic consequences. License information can be stored in any file of a package, not only package.json, so don't think of licenses as an afterthought!

  • Use tools like Bytesafe to identify license information in all files
  • Restrict problematic or unlicensed packages
  • Scan all package files for problematic licenses. Get notified when license issues are identified

4. Enable a dependency firewall to block packages

Great, so now you use a private registry and scan your packages for security and license issues!

This is when you should enable rules (policies) to block “bad packages at the door”, which means that they will not even be available in your registry. Making sure you enable policies that enforce that packages must be scanned, secure and do not contain restrictive licenses to be added to your private registry is a good start.

More information on how to use policies to block packages.

5. Shift responsibility to teams instead of single-point individuals

Even when you use a private registry you should always make intentional changes when you add new packages to a project.

The more packages you use, the greater the risk that one of those packages contain a security vulnerability. Therefore a best practice is to have more eyes at hand and to have the right tools to do wise decisions when adding new dependencies.

Tools like Bytesafe, that cache and visualize dependencies for all its users, can be used to democratize this information and make it available to all team members.

6. Avoid running scripts by default when installing packages

Do you know that executing scripts is part of the installation process? Very convenient and useful, but unfortunately this feature has also been used by hackers to execute random scripts. Therefore it’s a major risk to execute eg. post install scripts, unless you have checked them.

Unless you are confident and know what’s executed, you should install with the --ignore-scripts flag.

npm install PACKAGE@VERSION --ignore-scripts

7. Be aware of typosquatting risks

Which package is the official one — twilio-npm or twilio? Typosquatting attacks, which means attackers create packages with similar package names as the official ones. The intent is to get developers to download malicious packages.

Make sure you double-check what you install and preferably use curated private registries.

(BTW, twilio is the official package name)

8. Keep your tokens and credentials secure

If you are publishing packages to a public repository, it’s a good idea to centralize the token management. Store the maintainer token and publish with Bytesafe. Avoid the risk and hassle of distributing the token to all developers.

Avoid accidental exposure of sensitive credentials. Even though npm has added features to detect secrets, make it a habit to update your ignore files (e.g. .npmignore, .gitignore etc).

9. Have your different environments to use the exact same package versions

Getting consistent and deterministic results across different environments is an ongoing issue for any dev team. Depending on your setup and the state of if your project files are perfectly in sync you might end up testing other package versions than those intended.

Sound’s like an easy problem to fix, but in fact, your team needs to be quite skilled (package-lock, yarn-lock shrinkwrap etc) to achieve 100% deterministic result. Using npm ci (npm clean install) helps to replicate a specific state of node_modules, but does not cover transitive dependencies (dependencies of dependencies).

Any easier solution? For teams that require fully deterministic results, Bytesafe offers the Freeze policy ❄️. The freeze policy makes a whole registry read-only allowing for fully consistent results.

You are able to snapshot exactly what versions were used and use that snapshot regardless of environment. As ALL dependencies are frozen, this also includes transitive dependencies that are reproduced exactly.

10. Make sure the whole team uses the private registry

Wow, you ticked everything above! Now you just need to get everyone to use your private registry where you can trust your dependencies. This way you control the flow of the npm packages used in your team or company.

npm config set registry 'https://example.bytesafe.dev/r/default/'

--

--