You’ve probably heard that software supply chain attacks are increasing rapidly and that the damage can be devastating. Both business and security leaders have taken notice and are increasingly motivated to address this rapidly growing attack vector. But where should organizations begin, and what are industry best practices to proceed?
Enter SLSA (Supply-chain Levels for Software Artifacts), a security framework and a common language for improving software security and supply chain integrity. It is a cross-industry collaboration, maintained as part of the OpenSSF, that is based on concepts Google has been using internally since 2013 for all of their production software.
SLSA is focused on protecting software from source through its deployment by allowing users to make automated decisions about the integrity of the artifacts they are using, thereby preventing many possible attacks throughout the supply chain.
Figure 1. Where threats and risk occur in a software supply chain.
Adopting SLSA in Stages
SLSA consists of a set of requirements that are becoming the recognized best practices in the industry. These requirements can be adopted in phases or levels, with an improved security posture for each additional level attained. Broad adoption of SLSA can have cascading benefits throughout the software industry. As more companies produce and publish software with attestation based on corresponding levels of SLSA, consumers of that software can automatically verify its components and include them in their own software supply chain with greater assurance and trust.
Let’s take a closer look inside SLSA. SLSA requirements are split into the following categories:
- Source requirements (e.g. version controlled, verified history, two-person reviewed)
- Build requirements (e.g. scripted build, ephemeral environment, isolated, parameterless, hermetic)
- Provenance requirements (e.g. available, authenticated, dependencies complete)
- Common requirements (e.g. security, access, superusers)
SLSA Level
SLSA requirements are arranged into four levels, where each level further improves the supply chain’s security posture. The following table gives a high-level description of each of the SLSA levels and example controls implemented at those levels:
Level |
Description |
Examples |
1 |
Documentation of the build process |
Unsigned provenance |
2 |
Tamper resistance of the build service |
Hosted source/build, signed provenance |
3 |
Extra resistance to specific threats |
Security controls on host, non-falsifiable provenance |
4 |
Highest levels of confidence and trust |
Two-party review + hermetic builds |
The use of explicit levels has a few nice consequences for adopters:
- It helps define a specific security posture they want to achieve
- It defines the incremental progress required to achieve higher levels
Each level builds on the work achieved in prior levels, with higher levels putting more of the requirements into infrastructure components that form the supply chain: the build service, starting at level 2, and the source control service, starting at level 3. At level 4, all requirements have been fulfilled, giving the software consumer a high degree of confidence that the software has not been tampered with.
Anti-Tampering Via Provenance Verification
SLSA has its set of requirements, but how are they automatically verifiable? How does it protect against modifications which were not authorized by the software producer?
The verifiability is achieved by supplying evidence of adherence to the SLSA requirements. This evidence is captured in the form of an attestation - some metadata that effectively describes the artifact being attested to (typically by a cryptographic digest), how the artifact was produced, and the authenticating actor who generated the attestation. This is achieved through the in-toto based SLSA provenance format, which captures the following information:
- subject - the software artifact(s) information (name and digest)
- builder - the entity that produced the artifact(s)
- invocation - the event that kicked off the build, describing the execution information
- buildConfig - a record of the steps which were executed during the build
- materials - other artifacts which were used as input
- More fields and details available in https://slsa.dev/provenance
This provenance is meant to be published alongside the associated software so that those using the artifacts can verify them automatically and make policy decisions based on these attestations.
An example for a SLSA provenance attestation file, describing how an artifact named “FooBar” was created, given that its source code resides on GitHub (https://github.com/foo/bar.git) and it was built via GitHub Actions as part of a workflow build.yaml:
{
"_type": "https://in-toto.io/Statement/v0.1",
"subject": [{"name": "FooBar", "digest": {"sha256": "14ae5f..."}}],
"predicateType": "https://slsa.dev/provenance/v0.2",
"predicate": {
"buildType": "https://github.com/Attestations/GitHubHostedActions@v1",
"builder": { "id": "https://github.com/Attestations/GitHubHostedActions@v1" },
"invocation": {
"configSource": {
"uri": "git+https://github.com/foo/bar.git",
"digest": {"sha1": "3a1b6c..."}, // The git commit hash reflecting the version of the repo used for this build
"entryPoint": "build.yaml:build",
},
"metadata": {
"buildStartedOn": "2021-12-19T08:38:00Z"
},
}
"materials": [
{
"uri": "git+https://github.com/foo/bar.git",
"digest": { "sha1": "3a1b6c..." }
}, {
"uri": "git+https://github.com/actions/checkout@v2",
"digest": {"sha1": "5a4ac9..."}
}, {
"uri": "git+https://github.com/actions/upload-artifact@v2",
"digest": { "sha1": "e448a9..." }
}, {
"uri": "pkg:deb/debian/stunnel4@5.50-3?arch=amd64",
"digest": { "sha256": "e1731a..." }
}, {
"uri": "pkg:deb/debian/python-impacket@0.9.15-5?arch=all",
"digest": { "sha256": "71fa2e..." }
}
]
}
}
Producing and storing unsigned provenance offers a basic level of code source identification and can aid in vulnerability management (e.g. you can check if one of the materials components corresponds to a vulnerable log4j component). This will get you to SLSA 1. But in order to protect yourself from tampering and reach SLSA 2, the generated provenance should be cryptographically signed. The generated file will have the following format:
{
"payload": "ewogICJzdWJqZWN0IjogWwogICAg...", // BASE64(provenance)
"payloadType": "application/vnd.in-toto+json",
"signatures": [{
"keyid": "my-builder-key",
"sig": "MeQyap6MyFyc9Y..."
}]
}
So now we can create some policy checks, which should be validated before using the artifact in order to prevent certain types of supply chain attacks:
- Implicit checks: we can verify that keyid was used to create signature and that signature validates payload. Once we decode payload, we can verify that keyid referenced in the envelope corresponds to the builder id used in the payload.
- Explicit checks: we can verify specific fields in the provenance, e.g. which builders are allowed to produce these artifacts (for instance, allow only some specific self-hosted runner), which job should be used (gets checked against the entryPoint), etc. Moreover, we can indicate which minimum SLSA levels are required for the source and builder provided in the provenance.
Use Case Example - Package Hijacking Attack
Before: Successful Attack
Observing the risks graph in Figure 1, let’s examine the case where malicious actors are trying to get users to use a corrupted package (Case G - compromised package repo). This can occur if the attackers have managed to steal credentials to a container repo such as JFrog Artifactory. They use those credentials to upload a malicious version of a certain image. Then, during a routine update, the users will download and run the latest version of the contaminated container image, executing the attackers' malicious code.
After: SLSA Level 2 Prevents This Attack
Being SLSA 2 compliant is a game-changer in this situation. If the build process of the image produces signed provenance, there can be a controller component that implements a SLSA policy checker that uses a published source of provenance signed by the builder to check the image against a policy before running the image. The attackers cannot fake the provenance with the stolen container registry credentials, so the controller blocks the use of the evil image and the attack is prevented.
Legit is here to help!
The use case above is relatively simple, but there are many, many more attack scenarios possible across a modern, distributed software supply chain. Broadly adopting SLSA requirements is not an easy task, and also requires new security tools to monitors the various parts of the CI/CD pipeline from source to production. Legit Security is a big advocate for SLSA adoption, and has developed a platform to secure an organization’s software supply chain spanning the pipelines, infrastructure, code and people since 2020.
If you need help assessing your SLSA compliance, consider our free Rapid Risk Assessment.
If you’d like to learn more about common attack patterns documented by SLSA and other popular frameworks check out The 3 Riskiest Software Supply Chain Attack Patterns Across Frameworks.