Skip to content

Instantly share code, notes, and snippets.

@travi
Last active May 25, 2020 22:08
Show Gist options
  • Save travi/7f45e2ef4c34fe0d8eb004fbcbd5b16f to your computer and use it in GitHub Desktop.
Save travi/7f45e2ef4c34fe0d8eb004fbcbd5b16f to your computer and use it in GitHub Desktop.
Security considerations of automated npm package publishing

Security considerations of automated npm package publishing

Benefits of automated publishing

  • Enables Continuous-Publishing
    • the mainline branch always reflects the latest published version
    • contributed fixes are not blocked from consumption after merge because maintainer got distracted/lazy before publishing
  • Can automatically determine proper semver bump if commit convention is followed
    • Manually scanning the commits since the last publish can be error prone, leading the the version miscommunicating the level of change to consumers
  • Works against committed code only
    • Publishing locally opens the opportunity to include changes that have not been commited to the VCS
    • Secrets could exist locally and be included in the published module
  • Fully repeatable, eliminating human errors
    • Can be easy to forget a build step before publishing manually
    • Might forget to update the changelog or publish a GitHub release
    • Could forget to publish to a pre-release channel and have consumers pull as an update from the latest channel before intended
    • if not using npm version, could forget to tag the proper commit
  • Versions can be published without human interaction
    • Saves maintainer from needing to wait for builds to pass before taking the step to publish
    • Encourages following engineering practices that provide confidence that publishing will only happen if behavior is verified to be as expected

Recommended steps for making automated publishing secure

  • Create a separate (bot) user for publishing on both npm and GitHub. Limit publishing rights so that only this account is allowed to publish
  • Enable 2fa for logging into the bot account (auth-only). Severely limit who has the ability to log in as this user.
    • Creating tokens will also require entry of a 2fa code
  • Be sure to deactivate any tokens that are not actively being used for publishing in case they become leaked for any reason.
  • Create a personal access token for the GitHub account that enables the minimum permissions needed for publishing releases through the API.
  • Create read-only tokens for situations where publishing is not required. This is mostly useful for installing private packages in a CI environment.
  • CIDR restrict the publish tokens to the infrastructure that they are expected to be used on.
  • Leverage the secrets management system offered by your CI service, such as encrypted variables on Travis-CI or encrypted secrets for GitHub Actions

Considerations for publishing with 2fa

Enabling 2fa as auth-only was recommended above because the auth-and-writes option requires the ability to interactively enter a 2fa code at the point when a publish is triggered. When publishing from an automated environment, like CI, there is no opportunity to enter a code interactively. Because of this, the npm docs for requiring 2fa for package publishing even provides the following note:

Note: Currently, publishing a package with 2FA enabled on CI is not possible. For more secure CI publishing, enable 2FA on the npm account used for CI, and select "Authorization" only, and create a CIDR-restricted token for CI by following the steps in "Creating and viewing authentication tokens".

The implementation of 2fa for npm uses TOTP, which requires a code generated based on the current time and an initial seed key (or barcode) provided at the time of enabling 2fa. This means that you must have a device/app capable of calculating the code based on this information, making the codes something that cannot be memorized and also time-limited and not useful if stolen on their own.

Possibilities for providing a 2fa code when publishing in an automated environment

Run a TOTP generator in the CI environment

Using a tool like otplib and providing the original seed as an encrypted variable can enable calculating the 2fa token at publish time.

Risks
  • If an attacker can access the npm publish token, they also would have access to the 2fa seed. This would allow them to generate codes based on the seed and the current time, completely defeating the effectiveness of enabling 2fa.
  • Having both the npm token and the 2fa seed available from the same location effectively makes them a single factor.

Enable code generation in a web-accessible service

Save the 2fa seed in a separate service, such as optic that is able to generate codes based on that seed and the current time upon request.

Risks
  • If an attacker can access the npm publish token, they also would have access to the secret needed to call the code generating service securely. This would allow them to generate codes based on the seed and the current time, completely defeating the effectiveness of enabling 2fa.

Publish to a proxy service

Publishing to a proxy service, like wombat-dressing-room, enables encapsulation of 2fa code generation away from the initial publish trigger using the npm publish token. The code can generated and added to the command to publish to the real registry after the service performs additional checks, such as those available in wombat-dressing-room

Risks
  • If an attacker can access the npm publish token, they also would have access to the secret needed to call the proxy service securely. This would enable them to perform the steps to satisfy the additional checks done by the service. This is somewhat less risky than being able to request a code and use it directly because knowledge of what the extra checks verify and the steps needed to satisfy those checks.
  • The proxy service could become an additional attack surface for an attacker to compromise between the step of triggering a publish and the artifacts being added to the true registry.

Publish to a staging service that requires human to approve publish

Publishing to a staging service that requires human approval to make the new version available, such as the one proposed to be added to the npm registry, enables that human to enter a 2fa code at approval time.

Risks
  • A human is required in order to approve release. This reintroduces the delay that some see as a problem solved by automated publishing.
  • Race conditions of staging multiple versions before approval can complicate version number bumps and further modifications in the VCS that assume that the staged versions either have been published (but might not end up being published) or are not published (complicating a continuous-integration workflow that assumes that the mainline includes the latest published versions)
  • CI verification would be performed before staging a version to be published, so assumptions around this could reduce caution if changes have occured between staging and version and it being published. Some changes would be visible enough to result in dropping the proposed version from staging and recreating with the other changes incorporated. Other changes might not be as visible, such as if an attacker is able to compromised the staging service.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment