NPM Maintainers Security Review

A review of NPM package maintainers account security

After brief investigation of the top 1,000 downloaded NPM Packages, we found that a number of the package maintainers accounts have insufficient protection against basic account takeover methods. This could affect a number of downstream projects, some of which help host basic and foundational infrastructure in modern, digital society.

In the tech world, we stand upon the shoulders of giants.

If those giants have weak security authenticating their identities, they may crumble.

To stand on the shoulder of a crumbling giant is a risky posture for even the most balanced among us.

NPM Package Maintainers Security Review

Have you ever wondered about supply chain integrity of NPM packages?

You're about to find out what 45 minutes of searching can give you.

npm-maintainers-call-to-action

If you're interested directly in the results, here is the spreadsheet referenced in the twitter message above:

NPM Supply Chain Security Spreadsheet

The rest of this post goes over a little history, purpose, methodology, mitigations, and reflections from this 45-minute excursion into the NPM supply chain.

About:NPM

From npmjs.com/about

npm is lots of things.

  • npm is the package manager for Node.js. It was created in 2009 as an open source project to help JavaScript developers easily share packaged modules of code.
  • The npm Registry is a public collection of packages of open-source code for Node.js, front-end web apps, mobile apps, robots, routers, and countless other needs of the JavaScript community.
  • npm is the command line client that allows developers to install and publish those packages.
  • npm, Inc. is the company that hosts and maintains all of the above.

Purpose of Review

Identifying and raising awareness of how fragile the npm ecosystem is to developers and consumers is a primary goal of this review.

If an attacker successfully injects any code at all, it’s pretty much game over

At the root of trust for npm packages is the package maintainer.

In the words of the paper, “Small World with High Risks: A Study of Security Threats in the npm Ecosystem”

The security of a package depends on the security of its maintainer accounts.

  • Zimmermann et al., USENIX Security Symposium 2019

The package maintainer is responsible for handling the lifecycle of a specific package, i.e., the releasing of new updates, deprecation of old releases, versioning releases, etc. If the package maintainer is compromised, one can assume all packages maintained by that individual are also compromised.

What you see, what you get?

Not all packages have the same quality with regards to continuous integration and continuous deployment. Packages can be manually built and released to NPM. This gives us a kind of “what you see might not be what you get” type of scenario.

Some package maintainers even go so far as to publish minified code to reduce the size of the package. This is to convenience the end-user who downloads the package as a dependency. However, this also introduces a level of obfuscation that might be an entry-point for a malicious actor.

I've been shared two great resources on how easy it is for an attacker to obfuscate their code in an NPM package.

One write up is from snyk, on lockfile injection, “Why npm lockfiles can be a security blindspot for injecting malicious modules”. It provides a detailed runthrough of how one might perform this type of attack and steps for mitigation.

The other resource being a medium post, “I’m harvesting credit card numbers and passwords from your site. Here’s how.", has a similiar attack model.

In my package.json I’ve defined the files property to point to a lib directory that contains the minified, uglified nasty code — this is what npm publish will send to npm. But lib is in my .gitignore so it never makes its way to GitHub. This is a pretty common practice so it doesn’t even look suspect if you read through these files on GitHub.

This is not an npm problem, even if I’m not delivering different code to npm and GitHub, who’s to say that what you see in /lib/package.min.js is the real result of minifying /src/package.js?

Perhaps the maintainer is bought out by a large corporation interested in corporate espionage, or coerced by a government or state entity that has ill-will toward a organization or person that uses that specific library package. The list of possibilities is somewhat endless, and people's prices and points-of-leverage vary.

Assume the necessary level of risk for your use cases.

History Has been Rough

The more dependents a package has (downstream packages that depend on it), the more damage it can have if it is compromised.

left-pad

For example, look at the historical incidents when a package is removed from NPM and the downstream dependents will break. Most javascript developers should know about the left-pad incident documented in the “kik, left-pad, and npm” npm blog post. A single dependency took down almost all core packages.

Imagine if the maintainer of left-pad decided to ‘get even’ instead of ‘checking out’. If the maintainer decided to add a postinstall script that rm -rf /, how many developers would question running the command with sudo privileges without even reviewing why their package install needs sudo?

Dangers in the Wild

Event-Stream

The event-stream attack was targeted against specific cryptocurrency companies. It was a ‘surgical strike’ in that it could have affected many more systems than it did, but it seems like it was designed to only target companies that had crypto-related dependencies.

NPM had a detailed blog post on this: Details about the event-stream incident

event-stream github issue

How was the attack performed?

He added flatmap-stream which is entirely (1 commit to the repo but has 3 versions, the latest one removes the injection, unmaintained, created 3 months ago) an injection targeting ps-tree. After he adds it at almost the exact same time the injection is added to flatmap-stream, he bumps the version and publishes. Literally the second commit (3 days later) after that he removes the injection and bumps a major version so he can clear the repo of having flatmap-stream but still have everyone (millions of weekly installs) using 3.x affected.

How was access granted?

he emailed me and said he wanted to maintain the module, so I gave it to him. I don't get any thing from maintaining this module, and I don't even use it anymore, and havn't for years.

#116 comment

Eslint

Postmortem for Malicious Packages Published on July 12th, 2018

On July 12th, 2018, an attacker compromised the npm account of an ESLint maintainer and published malicious versions of the eslint-scope and eslint-config-eslint packages to the npm registry.

Research Methodology

Initial method was fast, so pardon the sloppiness of it. I'm sharing it with you all just so you can have some sense of how easy it is to grab this data. And how little tooling is necessary to do a basic analysis of whose It deserves tool to streamline the various datapoints together. Expect this cleaner version in a new post.

Initial Data

The initial dataset was grabbed from this gist: Top 1000 most depended-upon packages

From there, I grabbed all the maintainers: for item in $(cat 01.most-dependent-upon.md); do npm view $item maintainers; done > top-1000-pkg-maintainers.csv

For the file, top-1000-pkg-maintainers.csv, I cleaned up with some commands to remove white-space, brackets, and made a comma-separated file with username,e-mails as the initial two columns.

Crowdsourced 2FA Info

From there, people started crowd-sourcing info about each individual maintainers e-mail security. Basically, they would go to the e-mail login page and see what 2FA methods were available for resetting the password and the weak 2FA methods would be recorded.

The next piece of information that would come in handy to assess damage of a compromised account would be number of dependent packages.

Collecting Total Dependents

Dependent Packages (Downstream)

Knowing how many downstream dependents rely on a maintainers package is useful for estimating how much damage a single compromised maintainer account could cause.

Getting this information was fairly straightforward for those risking a npm install for the package npm-author-most-depended

for user in $(awk -F ',' '{print $1}' top-1000-pkg-maintainers.csv); do
    num_deps=$(npx npm-author-most-depended $user | awk '{print $1}' | paste -sd+ - | bc)
    echo "$user: $num_deps" >> user-num-dependents.txt
done

Mitigation

As an end-user

Build it Yourself

Clone the package repo, build it, and host it yourself.

This is the safest option.

Protect Your Upstream Maintainers

Review the upstream maintainers security stance.

If they have insecure settings protecting their accounts, tell them!

If you are kind, mail them a YubiKey or some other type of u2f device. Convince them to drop SMS and recovery questions, even TOTP is safer.

As a Package Maintainer

Protect your e-mail account at all costs!

Don't Trust SMS, it is not safe. Don't believe me, go do some research. Here are two papers that confirm my beliefs:

Don't Trust TOTP

TOTP is phishable. It's not guaranteed safe and the secret seed is not guaranteed to be secure on the device it is stored on (e.g., your phone).

From Digital Identity Guidelines - Authentication and Lifecycle Management

Authenticators that involve the manual entry of an authenticator output, such as out-of-band and OTP authenticators, SHALL NOT be considered verifier impersonation-resistant because the manual entry does not bind the authenticator output to the specific session being authenticated.

Trust FIDO2

WebAuthN is an example of a FIDO2 implementation. Learn it. Use it. Be protected because of it.

Using a YubiKey from Yubico is probably the easiest way to get some guaranteed security safety points.

I gave a few workshops on basic usescases (setting up 2FA on Github, simple GPG setup, signing git commits, etc) at the 36c3–see my post 36c3 - YubiKey Workshop for more info on how my friends and I bootstrapped 100+ people with that device.

Trust U2F

U2F is the older (but still pretty good) version that FIDO2 replaced.

Next Steps

The ideas behind this post are not new. The NPM community and other package management projects have faced these types of issues since they were releasing packages.

Educate and Strengthen Maintainer Security

Identifying the most influential maintainers and ensuring they protect their assets should be a community-driven goal. Raising awareness of the situation is step one. Training people to have better security practices should be the immediate next step.

Insights Tooling

I plan on building tooling around these stats for javascript projects I build that rely on NPM. I want to know who the maintainers are that I put my trust in.

I also want to help maintain their security and possibly even donate my time or money to make sure they have resources to be more secure. I do not feel like I give enough love back upstream to the giants who's shoulders I stand upon.

Resources and Inspirations