How Packages’ External Resources Threaten Your Supply Chain
Table of Contents
Many developers already know that in some ecosystems, open source dependencies might run their custom code from packages when they are being installed. While this capability can be used for both good and evil, today we’ll focus on a legit use case that, when misused, can escalate and be used to compromise your organization’s supply chain.
If you haven’t guessed yet, I’m talking about downloading and linking external dependencies during the install process.
This is part of an extensive series of guides about software supply chain.
Why do some packages use external dependencies?
Most of the open source libraries that are distributed via the registries are self-sufficient. They incorporate all of the assets and materials needed to build, compile, or use a given package. However, there are cases where the package is either a binding (explained below) or uses a third-party package of a different language or technology in a way that prevents it from being shipped with the package.
To quote Wikipedia, language binding refers to “an API that provides glue code specifically made to allow a programming language to use a foreign library or operating system service (one that is not native to that language).”
How do library creators and maintainers deal with this? Upon installing the package, they download the external dependencies from the internet and use them during the build and/or execution process.
The risks of using external dependencies
While this isn’t necessarily a bad practice, it creates a couple of issues within your open source supply chain:
- The resources you download today may not be the ones you will download in five days.
- The lock file your package manager creates may not ensure proper versioning.
- The building process will require an active internet connection.
- The caches that package managers have may not work.
- Your SBOM’s validity is threatened since it might not cover the used and potentially disposed of resources fetched during the install process.
The way I see it, two of the biggest risks related to external resources not embedded in the packages themselves are the risks associated with them disappearing or being tampered with.
Instability of ephemeral resources
The world of open source software is often considered a free for all — for good reason. Projects and developers come and go. They change jobs, languages, and career paths. Having externally downloaded dependencies means that you cannot just “download and store” all of the things that are needed. The software that you have running today on your local computer or on a production server might not be usable in a month. The result of this instability could be quite disastrous: imagine one of your critical package’s resources disappears just before a really important fix is supposed to hit production.
Can we secure what we do not control?
If you visualize an open source software supply chain it may turn out that it is more of a tree than a chain. A tree with some of its leaves well hidden from your sight.
External resources that are being fetched during the install process are exactly like those leaves. You may well know (even personally) some of the developers of the packages you use. You know they won’t go evil, but are you sure they also know and trust creators and maintainers of the packages that they download?
A great example of such behavior is visible in a popular Ruby package called `rdkafka-ruby`. This package is dependent on the `librdkafka` package of a different author written in C. Upon installation, its content is being downloaded.
recipe.files << {
:url => "https://codeload.github.com/edenhill/librdkafka/tar.gz/v#{Rdkafka::LIBRDKAFKA_VERSION}",
:sha256 => Rdkafka::LIBRDKAFKA_SOURCE_SHA256
}
recipe.configure_options = ["--host=#{recipe.host}"]
recipe.cook
# Move dynamic library we're interested in
if recipe.host.include?('darwin')
from_extension = '1.dylib'
to_extension = 'dylib'
else
from_extension = 'so.1'
to_extension = 'so'
end
Mitigating the risks
There are few ways both package creators and package users can minimize the risks related to using open source components that rely on external resources.
Package creators
As a package creator you can try a few of the following to improve your library usability:
- Check if you could provide an alternative where the user is responsible for delivering the external resource to the build step. That way the resource can be fetched before the building process and stored as one of the artifacts, ensuring that even if it is no longer available online, the build process won’t be interrupted.
- Include the external resource in your package and provide a flag for controlling whether to use the package embedded resource or to fetch the most recent version from the internet.
- Always verify the integrity of the external resources based on their SHA checksums. In case the code that is being downloaded is tampered with, the checksum will change. This should stop the building process, notifying the end-user about a potential security problem.
Here’s an example of how to prevent potential tampering with the downloaded resource. If anything were to be changed in the external resource — which might happen — over time the installation process will be halted and an error message will be presented:
expected_sha256 = '1332761f31c198cb9825e6ccccda0b6a0e57daeb824870e8524df77f1592d149'
filename = "#{workdir}/libpg_query.tar.gz"
unless File.exist?(filename)
File.open(filename, 'wb') do |target_file|
URI.open('https://codeload.github.com/lfittl/libpg_query/tar.gz/' + LIB_PG_QUERY_TAG, 'rb') do |read_file|
target_file.write(read_file.read)
end
end
checksum = Digest::SHA256.hexdigest(File.read(filename))
if checksum != expected_sha256
raise "SHA256 of #{filename} does not match: got #{checksum}, expected #{expected_sha256}"
end
end
Package users
Unfortunately, as the end-user, your options are pretty limited. The most important thing is understanding the packages you are using and whether or not they use external resources like the ones described in this article.
Here are some easy ways you can check your dependencies to gain a better understanding of the packages you’re using:
- Try running the install process of your dependencies offline to make sure it will pass. You can cache the packages and then try to install them without an internet connection.
- Verify the packages you use, and check if you can replace or improve any of the ones that could pose a security risk to your organization.
- Use tools like Mend Supply Chain Defender to gain better knowledge about your open source software components and make sure to keep track of any changes introduced to your applications.
Securing your open source supply chain
At Mend we use Mend Supply Chain Defender to keep track of cases like this. We open issues and offer help when we detect that a package uses external resources, in the hopes of helping to make the whole open source software ecosystem safer and healthier for everyone.
Join us next time for more supply chain security news and security tips!