Open source security is a challenge that organizations are finally starting to address. Many are struggling to find a way to balance the risks versus the costs of using open source. Companies that rely on open source want to make sure that they are on top of their open source security game, without having to sacrifice speedy development and delivery.
Over the past few years, the tech industry, the open source community, and the security community have been investing more resources in addressing open source vulnerabilities. As the race to get in front of vulnerable components continues to heat up, it appears a creepy new kid is gaining attention on the block: malicious open source vulnerabilities.
The Devil You Know: Accidental Open Source Security Vulnerabilities
The open source vulnerabilities that we’ve become accustomed to reading about, and hopefully protecting ourselves against, are the accidental vulnerabilities.
This is the more common type of open source vulnerability that security and development teams get alerted on. An unintentional bug in the code that could leave an open source component open to exploitation. They are innocent — or negligent — mistakes, and aren’t intended to be malicious. They can occur at any point in a package’s lifetime, perhaps at multiple times in its lifetime, and can be found in both popular and unpopular packages.
The innocent open source vulnerability is disclosed and announced publicly, usually along with a fix so that users can update the vulnerable version and stay secure. These types of vulnerabilities are manageable. When users stay on top of updating their open source components, they are usually secure from this type of threat.
The New Kid on The Block: Malicious Open Source Packages
Over the past year or two, a new type of open source security vulnerability has become notorious, though they are far less common than the accidental open source security vulnerability.
Malicious vulnerabilities, or supply chain attacks, are intentional exploits rather than coding mistakes. They are usually designed to do harm the instant that they are run or even installed, as opposed to innocent vulnerabilities that might exist in a library for years without impacting it. Malicious packages impact nearly everyone who uses them.
Typically, a “good” package turns “bad” for one or more releases. Usually in these cases, a package that was created to do something useful is hijacked, and a new release is made where a malicious payload or backdoor is added.
While open source packages are scanned for typical bugs and oversights, the malicious vulnerabilities usually aren’t discovered in a routine automated scan, rather by accident, potentially months later. This is what happened with the much-publicized event-stream vulnerability, where a developer stumbled upon the malicious package.
In these cases, disclosure is usually chaotic, published via a statement on social media or on the project’s board. While unintentional vulnerable open source packages are much more common, with hundreds of new open source vulnerabilities published every month, it’s the often accidental discovery of the less common malicious packages that seems to invite chaotic reactions.
The Story of a Malicious Open Source Vulnerability
A theoretical example of an entry-level malicious attack would go something like this:
- A hacker steals the artifact publishing credentials of an open source package, and proceeds to publish a malicious release.
- Lucky for us, the malicious script kiddy doesn’t have the credentials to change the source code too.
- Eventually, the accidental discovery will occur: someone will notice that there’s a release on npm, Maven Central, or RubyGems, with no release on GitHub, and the package will be taken down.
A more advanced malicious player can be the maintainer of a popular package, with the ability to publish a bug fix to GitHub and add a corresponding malicious payload to npmjs or RubyGems. This more sophisticated attack could go undetected for months or even more.
Why Do Malicious Vulnerabilities Work?
Malicious vulnerabilities aren’t caused by one mistake or oversight, rather an unfortunate series of conditions and circumstances.
First, nobody has the time to review all of the source code in the open source packages they use. Most mistakenly assume that if there was a problem then someone else would have already noticed it. The next problem is that even if you take the time to read the “source code,” a sophisticated attacker wouldn’t put their exploit into the raw source code, e.g., on GitHub. Taking it one step further, even if you attempted to download and verify the published artifact from the registry, it might be so transformed by its build process that it would be nearly impossible to find exploits. The final problem is that most package managers are designed to propagate new releases as quickly as possible, and do their best to provide users with the latest version. Naturally, they don’t know the difference between regular and malicious releases either.
All of these factors make it easy for a malicious vulnerability to go undetected. That said, there are ways to minimize the risk.
Minimizing the Risk of Malicious Open Source Vulnerabilities
There are a few best practices that you can adopt to make sure you aren’t automatically downloading and running a malicious version.
First, pin third-party dependencies to exact versions. When available, use a lock file to freeze dependencies in place, so that new versions aren’t automatically installed as soon as they are released. This means that unless you’re particularly unlucky, any malicious exploits discovered within the first few days have hopefully not made it into your install yet, even if that package is part of your dependency tree.
Finally, perform your package upgrades intentionally, not invisibly or automatically using commands like “npm update.”
While malicious open source vulnerabilities have caused quite a stir lately, there are ways to manage open source vulnerabilities and reduce risk. The key is to know what you’re using and make sure you have security in mind even in the earliest stages of development.