Introduction We recently sat down with our Vice President of Product, Tom Barbour, to discuss his strategy and vision for Kitu's products and how we can continue to evolve and develop. Tom has been...
In our previous blog post, Nine Best Practices for Defending Against Supply Chain Attacks, we discussed supply chain attacks from a high level and offered best-practice advice for defending against them. Using this as a base, we will now take a deeper look into a specific type of supply chain attack as it pertains to the Node.js ecosystem: NPM Manifest Confusion.
Understanding NPM Manifest Confusion
The Node Package Manager, or NPM for short, is considered one of the world's largest software registries. NPM exists as three distinct components consisting of a website, command line interface (CLI) tool, and registry. The CLI tool is how most developers interact with NPM, and it can be used to install, publish, and share Node.js code distributed as packages through public or private repositories, and it is also capable of robust dependency management. When code is published as an NPM package, a package tarball is created that contains the package metadata (typically in the package.json file), the actual source code, the package license, and any optional documentation, scripts, and binaries that the author wishes to include.
The metadata for a specific version of a package is submitted independently from the package tarball used to publish the same version and these are not validated against each other. Because the version metadata is never validated against the actual package of contents, this presents an issue that makes it unclear where the source of truth should be for a package's dependencies, license, scripts, etc. This issue has been named NPM Manifest Confusion.
From the surface level, the version metadata not matching the package metadata may not sound terribly problematic. While it may not matter if the author list isn't exactly the same between the two, more serious changes such as a license discrepancy could have a large impact on projects with specific compliance requirements. Even more concerning, a bad actor could take advantage of NPM Manifest Confusion to sneak in malicious scripts that would execute at install time, which would only be detected by carefully examining the version metadata, package metadata, and actual package contents. The level of scrutiny required to catch this kind of attack can quickly become unwieldy when you have a large project that pulls in hundreds of dependencies, especially if you leverage tools that only check the version metadata for a given package.
Mitigating NPM Manifest Confusion
Now that we have an understanding of what NPM Manifest Confusion actually is, let's delve into actionable steps that can be used to help mitigate this threat and improve the security of our Node.js projects.
- NPM Version: Utilize NPM version 7 or above to take advantage of security improvements related to dependency resolution and lockfile format.
- Explicit Dependency Versioning: Always specify the exact versions of your dependencies in the package.json file. This helps to avoid unintentional upgrades and ensures that your project uses the intended versions of packages.
- Audit and Monitor Dependencies: Regularly audit your project's dependencies for vulnerabilities using tools like Synk, SonarQube, and NPM audit. Set up continuous monitoring to receive alerts for new vulnerabilities that may affect your project's dependencies.
- Namespace Awareness: Be conscious of package names and avoid generic or easily confusable names. Leverage scoped packages to create a unique namespace for your organization or project, reducing the likelihood of a naming collision with malicious packages.
- Cache Considerations: Where possible, avoid using NPM's "--prefer-offline" and "--no-package-lock" options together to help protect against local cache poisoning.
- Use Private Registries: When possible, consider using a private NPM registry to maintain control over the packages your project relies on. Private registries allow you to curate a list of trusted packages, reducing the risk of accidentally including a malicious package from a public registry.
- Implement Continuous Integration Checks: Integrate automated checks into your CI/CD pipeline to catch potential issues early in the development process. Include dependency scanning, linters, and security checks as part of your CI/CD workflow to identify and rectify vulnerabilities before deployment.
- Educate Development Teams: Foster a culture of security awareness among your development teams. Educate them about the risks associated with NPM Manifest Confusion and supply chain attacks. Encourage a mindset of vigilance when adding or updating dependencies.
- Regularly Review and Update: Periodically review your project's dependencies and update them to the latest secure versions. Stay informed about security releases and patches for the libraries you use.
NPM Manifest Confusion poses a real and evolving threat to Node.js projects, highlighting the need for proactive security measures within the software development lifecycle. By adopting the best practices outlined in this blog post, you can significantly reduce the risk of falling victim to NPM Manifest Confusion and reinforce the security posture of your Node.js projects.
Remember, security is a dynamic process that requires ongoing attention and adaptation. Stay informed about emerging threats, leverage automation for continuous security checks where possible, and foster a security-conscious culture within your development teams. By doing so, you not only protect your projects from NMP Manifest Confusion but contribute to the resilience of the entire Node.js ecosystem.
The next blog in our Cybersecurity series will investigate various methods of secret and credential management and discuss why robust secret management is important even for internal-only projects.
Author: George Cagle, Kitu's Director of Platform Engineering
Interested in hearing more from Kitu Systems? Subscribe below!