The risks of supply chain attacks have gained visibility lately in the wake of the SolarWinds and Codecov hacks. Attackers compromise upstream code providers to sneak malicious sources into software products.
Many applications download external dependencies during their build routines. Using those downloads as-is can be dangerous. How do you know the file on the server hasn’t been replaced with a malicious version?
curl https://example.com/install-script.sh | sh
This command illustrates the simplest of supply chain attacks. Piping an install script into your shell is convenient but risky. If example.com
has been compromised, you’ve just given an attacker unfettered access to your system.
File Checksums
You can mitigate the dangers of this approach by checking if the downloaded file matches a known checksum. Many reputable open-source projects will publish a checksum that you can compare against. Only run the file if its calculated checksum matches your known value.
Implementing this check into your build scripts can quickly get repetitive. Preflight is a tool that helps you perform checksum comparisons so you can avoid common supply chain attacks.
Preflight accepts a binary’s path and a checksum to compare it against. If the file’s checksum matches, it’ll be executed as normal. Otherwise, the command is aborted, guarding your build script or CI pipeline against unintended malicious code inclusion.
Using Preflight
You can download Preflight from its GitHub releases page. The tool’s also distributed as preflight
in the Homebrew package manager.
At its simplest, Preflight can be used anywhere you’d normally reach for | sh
. Pipe another command into Preflight, supplying a checksum to match before anything is executed.
curl https://example.com/install-script.sh | preflight run sha256=abc...123
Now the install script will only be executed if it matches the known checksum. This gives you confidence that the file’s content hasn’t been tampered with. Preflight supports the sha256
, sha1
and md5
hash types.
You should hardcode known checksums into your build scripts. Don’t download a checksum file provided by the same server as the target file. Assume an attacker with the ability to modify the script has also uploaded a bogus checksum file.
Programmatic Checks
Sometimes you might want to take an action depending on whether a file successfully matches its checksum. In this case, you can use preflight check
to perform the comparison without actually executing the file.
curl https://example.com/install-script.sh | preflight check sha256=abc...123
The command will exit with status code 0
if the file’s checksum matches. A status code of 1
will be issued when there’s a discrepancy. Preflight will also emit an error message to the standard output stream.
Creating Hashes With Preflight
Not all software projects publish hashes for their releases. Preflight has built-in support for generating a new hash if you need one for a dependency.
Download the file from the vendor’s website. Then pass it into preflight create
to get the hash of the file’s contents.
wget https://example.com/install-script.sh
preflight create install-script.sh
A SHA256 hash is generated by default. You can switch to SHA1 or MD5 by adding the --digest
flag to the command.
Adding Malware Scans
Beyond basic checksum comparisons, Preflight supports optional malware scanning. This feature scans your file against known malware signatures.
Malware lists are provided by third-party services. Preflight works with any text file containing a list of known malware checksums. Malshare is one provider of these lists.
Set the PF_FILE_LOOKUP
environment variable to point to your file’s location. Preflight will detect this variable and enable malware look-ups. When you run preflight check
or preflight run
, the target file’s checksum will be compared against the entries in the malware list. The check will fail if there’s a match.
What About Other Kinds of Dependency?
Preflight focuses on checks of static files. It’s best suited to pre-built binary downloads which you acquire directly from the vendor.
It’s less realistic to use Preflight with dependencies acquired via package managers. Risks arising from software installed via npm, Composer, NuGet, or Maven will need to be handled in a different way. You can use package manager features such as vendor scoping to ensure dependencies get installed from trusted repositories.
Preflight should be one component in your defense against supply chain injections. Analyze your build scripts to identify intrusion vectors provided by other forms of third-party software.
Summary
Supply chain attacks are a growing problem that affects the security of software deployments. Many developers incorporate direct software downloads into their build scripts. This could give attackers a way to include code in a target project, by compromising an upstream vendor.
Preflight simplifies security checks of third-party scripts and binaries. There is a certain irony though: by using Preflight, you become reliant on a third-party package that you will need to download. You can mitigate this by building Preflight from source yourself and hosting the binary on an internal server that you trust.