The ease with which you can add new packages to your project using NuGet, NPM, Maven and other package managers comes with a quite substantial security risk. Do you know which packages you’re using exactly? Do you know who built them and who is maintaining them? Do you simply update the versions whenever there’s a new one, or do you review new version? Did you spend time going through the open source codebase of the packages to see what it is they exactly do?
In a lot of cases, the answers to these questions is: no. You don’t, because that all takes a lot of time and you didn’t import the package to have to spend a lot of time on it. You imported it because it saves you the time from writing that same functionality. Don’t get me wrong: that’s a GOOD thing. But you do need to be aware of the consequences.
Quite recently, the author of event-stream on GitHub came clean and provided information about how an unknown person was granted access to maintain an old version of some open source code. In this particular case, “maintain” means having access to the GitHub repository, but also publish permissions on NPM. What sounded as harmless in the beginning, turned out to be a hack that got downloaded about 8 million times. For more background information, check out the GitHub issue here: https://github.com/dominictarr/event-stream/issues/116.
This shows clearly what risk package managers introduce to any project using them: untrusted code will make its way into your project and serious security issues arise because of this. In a lot of projects, the upgrading of packages is considered to be best practice, also because newer versions might include security fixes. But almost never are people concerned with new security issues coming in with newer versions of a package. The above GitHub issue makes clear once more that this risk is real.
Dependency hell comes in a lot of forms. All developers will have to deal with it at some time, no matter what language or frameworks you use. That in itself is not the fault of package managers, but package managers do hide a lot of the complexity.
To begin with, lets agree on one thing: each package you select will add a dependency for your project. That in itself is fine, as long as you keep using that specific package with that specific version. But you won’t, because the package will get updates. That’s a good thing of course, features will be added, bugs will be fixed and security flaws corrected. So far so good. But once you start adding more packages, chances increase that you’ll get into situations where the dependencies do not align any more. For example:
- You’ve imported Package A, version 1.1
- Package A depends on Package B, version 2.5
- Now you want to add Package C, which also has a dependency on Package B, but the version should be 2.4 or lower because the developer of Package C did not use the newer version yet. Hmmm… now what?
Example of how dependencies are resolved in NPM (source)
Some of these problems can be solve, some simply cannot. There are situations where you cannot get this fixed without upgrading or downgrading one of the packages. And that stinks, but it’s the way it is. If there’s ever anyone in your team who states technology X does not have dependency issues at all, be very, very skeptical about this because most of the times this is impossible.
SO, WHAT NOW?
Having read the above you might wonder: ok, so what now? I don’t have the perfect solution, but luckily there are good ways to minimize risk. Here’s a few things to do:
- Make sure your team(s) feel(s) responsible for both quality and security. Although tools and pipelines can help prevent a lot of trouble, it should be the developers foremost who are conscious about what packages they use, why they use them and how trustworthy they are. If you have developers in your team who add new packages without thinking about the risks involved, teach them. They might not like it, fine; point them to posts like the above one so they understand the risk is real.
- Use tools in your development process that help to reduce the risk. Catch dependency hell in your project by continuous integration processes that ensure dependency issues become visible as early as possible. Analyze the packages you use and the dependencies they have. Make a clear decision on when to upgrade and how often. Having a little bit of process in place can help, even though not everyone might like it.
- Use tools that scan for possible security problems. Tools like SonarQube, Snyk, CheckMarx, GitHub and others can help to scan your project for security related issues. These tools can help check the packages you’re using and any security related issues that might be known. That’s important by the way; this only helps when the issues are known. If you want to prevent still unknown issues, consider not upgrading packages the minute that a new version is out.
I want to stress once more: these measures only help to reduce risk, there will always be some risk left. But if you can prevent 99% of the issues, you’re already way ahead of the competition.