On October 22, NPM and GitHub announced that the popular UA-Parser-JS library had been hijacked for distributing malware. Some of the world’s most popular software applications use the library, from the likes of Apple, Mozilla, Elastic, and Facebook. This attack demonstrated how attackers can easily weaponize a small piece of the software supply chain on a global scale.
What put this attack on steroids was not the dangerous payload, but the way it exploited how automated updating systems download the latest versions of software and push them into production quickly. Because these systems trusted the source of the UA-Parser-JS library — which was a lightly-defended NPM package — many sophisticated technology organizations installed the malicious packages as part of their continuous integration processes. The incident highlights why we need to rethink the upgrade process and make it smarter for the age of supply chain attacks.
Conventional wisdom holds that automation reduces the attack surfaces and improves security posture. But in many cases, automation can backfire, opening new, unforeseen gaps and expanding the attack surface, as the UA-Parser-JS attack demonstrates (in cases where packages are upgraded by default in the manifest SBOM). Security teams need to consider whether they need an upgrade or whether it increases or reduces attackability. With attackability, security teams have to decide how easy or hard it is for hostile forces to attack and penetrate the company’s infrastructure and applications.
Rather than updating as quickly as possible, application security and developer teams should consider adopting a more intelligent approach that considers whether upgrades are necessary or even desirable. This hybrid approach combines automated analysis of upgrades and their impact with human-powered wisdom.
Exploiting the software supply chain
The logic behind this has become clear. Developers love to replace first-party code with third-party libraries because it means less code to write for repeated and boring functions like identifying a browser engine. The way NPM and other package managers work, packages like UA-Parser-JS are bundled with other packages used in an application. The application owner tells NPM to automatically update all package dependencies once per week, for example. This saves the developers the pain of having to run manual updates. In theory, it makes applications more secure.
Make versioning smarter
When package managers automatically update all third-party packages, it creates a security risk to the software supply chain. Today, by some industry estimates, more than 90% of all production applications contain open source code or libraries. With so much code beyond the immediate oversight of application security and development teams going into production, the risk for supply chain attacks has become magnified.
During the UA-Parser attack, a malicious hacker had hijacked control of the package and replaced the legitimate library code with three malicious versions. Those hacked versions enabled cryptocurrency mining, password-stealing attacks, and malware insertions on Linux and Windows systems. An analysis revealed that the hacked version dropped a DLL that steals credentials from more than100 popular Windows applications—including Firefox, Chrome, FileZilla, Apple Safari Remote Desktop, and WinVNC. The attackers could easily have changed the package again to target other types of devices - iOS, for example.
A month rarely goes by without a supply chain attack on commonly used open source libraries or software packages. So, automatically downloading and installing upgrades to widely used open source software libraries and packages holds tremendous risks. For this reason, automating the upgrades of all libraries or other packages is the wrong approach.
I realize this goes against the trend of providing “security as code.” But we believe that a hybrid machine + human approach makes more sense. Using better tools to assess the impact of package and library upgrades accurately and quickly, and hunt unauthorized code changes, will let human operators judge whether a library or package is worth upgrading. An upgrade doesn’t make sense if there are no security fixes or vulnerabilities addressed. It’s especially true when mission-critical systems are exposed to the upgrades, either directly or laterally.
Organizations should adopt an “upgrade-as-necessary” policy and possibly even create a blue-green distinction between upgraded and non-upgraded code to test upgrades while insulating against infection. Open source and third-party code are not going away. Package managers make it possible to manage complex sets of dependencies easily. Modern architectures will require these constructs. But we can make them safer while still maintaining efficiency by getting smarter with automation, adding context, and factoring in attackability when versioning open source software.
Chetan Conikee, chief technology officer, ShiftLeft