MED Fadel Moumeni
Linkedin
Nowadays, developers rely heavily on packages (dependencies) to build products. These packages enable developers to focus on business logic instead of spending time on technical implementations. They also facilitate sharing logic between services or applications. However, these packages come with significant challenges, particularly in terms of security.
The problem of dependency confusion arises when there exists a public package with the same name as one of an organization’s private packages, causing ‘confusion’ for the package manager in use on which of the packages to fetch.
Here, we can easily observe that the public registry contains a version higher name-package@2.0.0
than the one available in the private registry name-package@1.0.0
Therefore, the package manager (npm) will select the latest version of the package, which is the one available in the public registry.
After conducting reconnaissance, an attacker might discover the project's package.json
file, which lists all its dependencies, as shown below.
Here, we can easily observe that some packages, like express
, are public and accessible. However, the packages highlighted in red are not available in the package manager (npm).
At this point, the attacker can easily create and publish a malicious package (e.g., pplogger@0.5
), ensuring the version is higher than the one specified in the project's package.json
file.
Why does the attacker need to create a package with a higher version? If we examine the package.json
file, it specifies pplogger:^0.2
. When the npm install
command is executed, the package manager will fetch the latest compatible minor or patch version starting from 0.2
. This means that any newer version within the 0.0.x
range would be installed, provided it adheres to the semantic versioning rules for the caret (^
) operator.
All the attacker needs to do now is wait for someone to run the npm install
command on their machine, causing the malicious package to be installed on the target system.
This technique enables hackers to achieve Remote Code Execution (RCE), allowing them to upload backdoors onto both developers' machines and server systems.
Adopt a standardized naming convention for all internal packages by using company-specific identifiers, such as @company-name/package-name
to prevent packages from being overwritten or impersonated in public registries.
Utilize a private repository registry for internal packages (eg : GitHub Private Repositories), enforce namespace restrictions, and configure your package manager to prioritize private registries for these packages.
Utilize lockfiles (package-lock.json
, yarn.lock
, or equivalent) to lock dependency versions and ensure consistency across environments.