Security Concepts for Developers: Race Condition Attacks
What are race condition attacks? Exploring examples and mitigations.
Discover the hidden risks of using trivial packages in development. Learn how small, seemingly insignificant dependencies can lead to significant security vulnerabilities.
In March of 2016, the widely known and used JS compiler Babel, was broken by a sole individual. The package, used by tech giants such as Facebook, PayPal, and GoDaddy was not brought down by a malicious hacker but rather due to the removal of the left-pad module within the npm registry that Babel relied on.
This module that acted as the straw that broke the camel’s back was a mere eleven lines of elementary code that prefixed padding to strings. Here it is in its entirety:
module.exports = leftpad;
function leftpad (str, len, ch) {
str = String(str);
var i = -1;
if (!ch && ch !== 0) ch = ' ';
len = len - str.length;
while (++i < len) {
str = ch + str;
}
return str;
}
The developer of the left-pad module, Azer Koçulu, intentionally removed all of his 273 contributions to the registry after a dispute over another one of his packages named ‘kik’. The popular messaging application with the same name, Kik, via a contracted trademark agent by the name of Bob Stratton, had reached out to Koçulu with a request that he release the package name. After Koçulu declined, Kik threatened legal action and also contacted npm directly.
Ultimately, npm took the side of the company and turned the name over to Kik, leading to the disappearance of the left-pad function that so many projects relied on.
The left-pad module meets the criteria of what is known as a trivial package. A package is considered to be trivial if it is under 35 lines of code and simplistic. The terminology, coined by a research paper written by Concordia University, is used to describe dependencies that could easily be written by developers themselves.
In the analysis provided by the same research paper, performed across more than 230,000 npm packages and 38,000 JavaScript applications, it was discovered that 16.8% of packages could be classified as trivial.
Some noteworthy trivial packages include:
When you are facing a deadline, it may be tempting to use a prebuilt solution. Code reuse, in theory, is a great thing, as it allows a “work smarter, not harder” approach to development. Trivial packages, however, take this mentality to the extreme by outsourcing even the smallest tasks in your code.
In the study done by Concordia University, amongst the surveyed Node.js developers, the main response received for why trivial packages are used was that developers just want to use code that is well-tested and works. However, though over half of the developers stated that these packages are “well implemented and tested”, only 45.2% of trivial npm packages actually have tests. Additionally, 10.9% of all the applications evaluated in the study used trivial packages.
Trivial packages also, on average, receive less maintenance and updates and are slightly less likely to use version pinning. This lack of attention can lead to security issues such as dependency confusion and package hijacking attacks. Revisiting the left-pad fiasco, an inventory of all of Koçulu’s removed packages was taken, and within just a few hours after being unpublished, 230 of the packages were replaced with name squatting packages.. If the name squatter had malicious intent, anyone that did not remedy the situation brought about by the unpublished packages could have been compromised by a supply chain attack.
Despite these security threats, 57.9% of developers surveyed did not consider using trivial packages as bad practice.
Even these trivial packages have their own dependencies and those dependencies are dependent on other modules and libraries as well. Over 40% of trivial packages have at least one dependency, with 11.5% having more than twenty. This creates a chain reaction of dependencies, each one adding complexity and danger, neutralizing the intended use in the first place – simplicity.
This bloat in dependency overhead can create an overly complex dependency management process. Every single entry added to your package.json file can be a potential attack vector for supply chain attacks, expanding your attack surface exponentially due to the chain reaction of third-party reliance.
Even in the absence of a dependency confusion or package hijacking attack, this interwoven web can present issues all by itself. After the left-pad incident, npm released a statement that disclosed measures to make it more difficult to remove a package from their registry if doing so would break other packages..
This has since been abused. In January, a group of npm users uploaded a package named ‘everything’. This package caused a Denial of Service (DoS) for anyone that installed it, as it made itself dependent on every other public package in the registry. Due to the changes implemented by npm in regard to unpublishing, even though the developer of ‘everything’ wanted to remove the package after things got out of control – he was unable to do so himself. npm had to intervene themselves.
This is not even an isolated incident, in 2023 the no-one-left-behind package used the same concept of mass dependency and in 2012 the hoarders package attempted the same thing with modules.
Although even the most basic packages can reduce development time, the dangers greatly outweigh the benefits. To mitigate your risk level, there are several manual steps you can take to review your environment - you should be aware of each and every dependency your project relies on and their functionality. Create an accurate ledger or update an existing one and proceed with the following checklist:
View the release history of the dependency to ensure trustworthy entities actively maintain it. Those developed by large organizations with good standing or well known open source developers are more likely to be reliable. Consider how often updates are released and how active the commit history is. Are issues resolved in a reasonable time?
Research the vulnerability history of a dependency. Analysis of past vulnerabilities can give you an idea of how responsive maintainers are to security concerns. Thorough analysis can also give insight into vulnerabilities that have been persistent, even after patches were released. This can hint toward the level of security awareness behind the dependency.
Changes in ownership or maintenance should be investigated as changes can lead to unfamiliarity with the code base, inconsistent quality and security risks.
Ask yourself if the trivial package is providing something that you or your team could not implement yourselves. Even just the increase in time spent towards package management processes may very likely outweigh the initial time saved. Single functions should not be considered packages. Modules with a small set of functions should also be reconsidered.
Additionally, look for a natively integrated equivalent to reduce your reliance on third-party software. Is there a function within the standard library if you target more recent releases?
The trivial package in use may not even be written correctly or optimally. Review all packages with a small number of lines to see if they could be rewritten to increase performance. Even though Concordia University deems trivial packages to be less than 35 lines, this is not a stringent rule that must be adhered to. Take a liberal stance on what constitutes a trivial package when it comes to file size.
Furthermore, this variance in code quality between different contributors can create weak or vulnerable areas in your project. Even though they may have been pieced together in a way that works, this does not mean that they are interconnected in a manner that is secure.
Remove any dependencies that are no longer required for your project to work properly. These may include ones that were only used for development and are not necessary in a production environment. Instead of importing trivial packages into your project, directly integrate (vendor) the code into your project (with the appropriate license comments). These measures will reduce the supply chain attack surface of your project.
At Arcjet, we use Socket to analyze all our dependencies. This includes an alert on trivial dependencies so we can deliberately review any occasions where we use them.
By reconsidering the use of these trivial packages, you can reduce the risk of a security breach that could be attributed to something as insignificant as a couple minutes saved.
Adopting manual review practices can help reduce your attack surface and avoid unnecessary complexity when it comes to package management.
As third-party component integrations become more widespread - staying vigilant and proactive in dependency management will continue to grow in importance.
What are race condition attacks? Exploring examples and mitigations.
Discover essential strategies for managing developer secrets and preventing leaks in CI/CD pipelines, version control systems, and third-party dependencies.
Strategies used to deliver malware infested packages - via a dependency confusion attack - and how to mitigate them.
Get the full posts by email every week.