Regardless if you call it dependency confusion, substitution attacks or namespace confusion it’s all about injecting non-intended packages. Dependency confusion occurs when a user or system is tricked into pulling a package version from a public registry, instead of the intended package of the same name from a private registry.
And it has been the new supply chain attack that everyone has been discussing in 2021.
The question that everyone has been asking: How do you defend your supply chain from this issue? There’s been plenty of suggestions, including from us at Bytesafe:
- Use of scoped packages and claiming the namespace in the public registry.
- Using vetted registries, with only approved packages (Firewall registries).
Both these suggestions have merit, but they have one flaw. They require additional configuration and actions from the users to be secure.
We know that any solution that requires users to opt-in and make their own configurations will never be safe enough. For us, it was clear from the start that our solution should be safe out of the box.
We are happy to introduce our safe by default solution for Dependency Confusion: Internal packages.
How to avoid dependency confusion
One of the primary goals was to design a solution that does not rely on complex user configuration.
The solution in short:
- Package versions published, pushed or uploaded to an internal registry will automatically be flagged as internal
- Fetching new versions of internal packages from upstream sources, will only consider upstreams containing internal versions of the same package
It’s as simple as that. Packages flagged as internal will automatically be protected from dependency confusion.
Packages without the internal flag will function as they always have, with full access to public upstreams.
Users can continue to use Bytesafe registries for both public and private packages, enjoying the benefits of a single source of truth for all package dependencies. While simultaneously being fully protected from dependency confusion.
And the best part, all the complex logic behind it is handled by Bytesafe, instead of being pushed on to our users.
Working with internal packages
From a user standpoint the solution couldn’t be simpler: Internal packages are prevented from being fetched from external upstreams by mistake.
Let’s have a look at how that works in practice.
Internal registries & packages
Any registry in Bytesafe can now be flagged as internal. This is enabled by default when creating any new registry to make the solution as low-touch as possible.
Any versions pushed to an internal registry will automatically be flagged as an internal version.
Example: Publish private packages to a private registry, to be shared and available to other team members or systems:
$ npm --registry 'https://workspace.bytesafe.dev/r/your-internal-registry/' publish
...
+ your-internal-package@4.5.6
With the registry destination flagged as internal the version will be flagged internal as well.
Protection during dependency resolution (npm install)
The core functionality of internal packages is evident when resolving dependencies from upstream. This is typically the case when using npm install
to install dependencies for a project and npm
attempts to fetch all available versions.
Or when using bytesafe pull
or Bytesafe web to fetch new versions from upstream sources.
Any attempt to fetch new versions of internal packages from upstreams will only use upstreams that contain other internal versions of the packages as valid sources. All other sources will be ignored for the purpose of this action.
Example: Using a Bytesafe private registry together with registry.npmjs.org as an upstream for public packages:
- Package published to the private registry will be flagged internal
- Install of internal package by other users / systems will never fetch versions from registry.npmjs.org as it does not contain internal versions
- Installing any public package will work as normal, with registry.npmjs.org as a valid upstream
Internal registries does not equal private only (it can also contain public packages)
One important distinction is that registries flagged internal are not only for private packages.
Public package versions (like any package fetched from registry.npmjs.org
) will be completely unaffected by the addition of internal packages and registries.
Getting started - protecting internal packages
New workspaces and registries - No action required
The internal packages feature is active by default. New registries are created with the internal flag enabled.
All package versions pushed to internal registries will automatically be flagged internal (and be protected from dependency confusion).
For existing workspaces and registries
Protecting existing registries and packages is as simple as:
- Enable the internal flag for your registry
- Republish the latest version of all internal packages
Done! All the complexity when fetching packages will be handled securely by Bytesafe.
From now on, fetches of internal packages will only consider upstreams containing internal flagged versions of the package.
Upstream issues - additional layer of defence against supply chain attacks
Bytesafe also detects upstream configuration issues that could indicate a supply chain attack.
Upstream issues are opened when a package version is found in multiple external upstreams, with non-matching contents. Issues trigger notifications, warning you of a potential dependency confusion attack.
Visit docs.bytesafe.dev for more details
Most users will not require any additional configuration to be protected. But for users that require advanced configurations, like how to configure a trusted source outside of a Bytesafe workspace, make sure to check out the documentation on internal packages.
Want to try it out immediately? Login to your existing workspace.