
How to prevent software supply chain attacks
Supply chain attacks can enter through what you consume, how you build, and what you release, and a control at one stage won't catch a compromise at another.
An approved repository isn't full coverage. Know which dependencies are built from source in a controlled environment and which still rely on the upstream publisher's build, protected only by scanning, cooldowns, and available provenance.
Confirm that your policies block what they're meant to, and keep incident response ready. Prevention reduces the chance and reach of an attack but doesn't eliminate it.
Your team can route every dependency through an approved repository and still ship a compromised release. Without the right security controls in place, an attacker who steals credentials with package-publishing access could inject malicious code into a trusted package.
A compromise at one step can evade controls applied at another. To prevent software supply chain attacks, control what enters your software, protect how it's built, and verify what you release.
What are supply chain attacks?
A supply chain attack targets an organization's trusted third-party vendors, software, or hardware providers to infiltrate systems and spread damage to others further down the chain. Attackers compromise the supply chain rather than the organization directly, exploiting the systems, tools, or code that engineers use to build and deploy software. Because software components depend on one another, an attack on one point can affect organizations several steps removed from the compromised component.
There are several different types of supply chain attacks, but they most often fall into one of three categories:
Malicious code injected into a trusted source
The XZ Utils backdoor is a clear example of how tampering can span multiple source and release processes. XZ Utils releases 5.6.0 and 5.6.1 included malicious content that altered the build under specific conditions and targeted certain Linux configurations. Part of the backdoor appeared only in distributed release tarballs, while other malicious content was hidden in test files committed to the upstream repository.
Exploit of a vulnerable dependency
The Log4Shell incident exposed a remote code execution vulnerability in the Apache Log4j library. Exploiting a vulnerable dependency is different from deliberately inserting malware into its supply chain, but both risks belong in your dependency security program.
Malicious code introduced through a transitive dependency
This type of attack targets a transitive dependency — a package brought in by another dependency that might otherwise seem well-vetted. In the 2018 event-stream incident, a new maintainer added flatmap-stream as a dependency of event-stream. A malicious version of flatmap-stream then targeted the Copay application's build process. For any project consuming event-stream directly, flatmap-stream was a transitive dependency, one level below the package its engineers chose.
How to prevent supply chain attacks at every layer
With so many different possible attack vectors, it’s important to create a multi-layered strategy for supply chain defense. A hardened container won’t stop an attacker with stolen publishing credentials from releasing a malicious dependency. A valid signature won't tell you whether the signed package contains malicious code. Use complementary controls at each point where code enters, executes, or leaves your environment.
Identify dependencies, suppliers, and entry points
Work with your platform engineers to inventory direct and transitive dependencies, container images, and build tools, including plugins and workflow actions. Use dependency manifests, lockfiles, and Software Bills of Materials (SBOMs) to identify components and versions. Then inspect build scripts, package-manager settings, repository configuration, and retrieval logs to map where those components enter your environment. An SBOM alone doesn't establish which endpoint served a download.
Assess both the supplier and the component before adoption. Check maintenance activity, security reporting procedures, and responses to previous vulnerabilities. For service and build providers, assess access controls, provenance, and the build evidence they supply about their releases. Give the most scrutiny to components that execute during builds or can access credentials, since their compromise can affect multiple projects or releases.
Protect dependencies before they execute
First and foremost, keep package resolution to approved sources only, including explicit source rules for private packages. Review all packages admitted to your internal repository and prevent builds from bypassing it. Otherwise, an approved repository can coexist with direct downloads from an unapproved public source.
Once the approved source is selected, apply controls that address the various ways a package can become malicious:
Controlled source builds replace the upstream-published artifact with a package built from the expected source in a controlled environment. A controlled build approach reduces reliance on the upstream build and publishing infrastructure, where an attacker could insert code that isn’t in the source. (Pair this with separate defenses for malicious source code.) When a package isn't built this way, you're still trusting the upstream publisher's build and account security, and your protection comes from scanning, cooldowns, and any provenance the publisher supplies.
Malicious behavior scanning checks packages for signs of credential theft, backdoors, or malicious install scripts before they reach your environment. It can identify harmful code even without a published vulnerability advisory, though this detection is not exhaustive. Run scanners and package firewalls before installation where you can, so detected threats are blocked before code runs, and keep runtime monitoring in place for anything that gets past earlier checks.
Cooldowns delay newly published upstream versions, giving the security community time to identify threats before you pull them into your build.
Use minimal, hardened container base images
When you build containers, start with the minimal base that can support your application and keep unnecessary packages and build tools out of the final image. This reduces both your attack surface and the code you need to maintain.
Harden images by removing default credentials, disabling unused services, and consistently applying the latest patches. This includes golden images, which shouldn’t be “set-and-forget” snapshots.
Automate rebuilds of base images or use maintained container images from a supplier. To get those fixes into your application, rebuild your application image against the updated base, test it, and redeploy. If you pin a base image by digest, advance that digest deliberately when accepting an update. Rebuilding against the old digest keeps the old base.
Lock down and verify your CI/CD pipeline
Isolation and short-lived environments reduce opportunities for one compromised job to persist into later builds, so run your build jobs on ephemeral, clean instances that you destroy after use. Keep your CI/CD tooling up to date and hardened, including all security patches.
Use multifactor authentication for human access and the principle of least privilege for automation. Give each job only the permissions that it needs, and use short-lived credentials wherever supported. Keep release secrets away from untrusted pull requests and jobs that execute dependency code.
Pin container images to a digest so a moved tag can't silently change what runs. This will preserve the selected artifact, but doesn’t establish that its code is safe, so make sure you assess the code when reviewing updates and keep pins current.
Monitor your pipeline for unexpected workflow changes, unusual build activity, and deployments outside your approved release process, which could signal a compromise.
Automate SBOM generation and verification
Every build should automatically produce an SBOM that lists the included components and dependencies, including transitive dependencies. If a vulnerable or compromised component is identified, an SBOM can help you react faster. For example, an accurate SBOM could help you locate any applications containing an affected Log4j version.
Bind the SBOM to the artifact it describes and retain the mapping to deployed releases, so you can find what is actually running. Just make sure to validate required fields and check coverage against the built artifact and record any known gaps.
Ensure strong provenance and cryptographic signing
Provenance refers to metadata that indicates who built what, when it was built, and how. A signed build attestation can state that a particular binary came from a specific source revision through a named build system.
Before release or deployment, verify your build against this provenance. Check the signature against a trusted signing identity, confirm that the attestation identifies the exact artifact digest, and compare the source repository and revision, builder, and build parameters against your team’s approved expectations. Stop the release when required evidence is missing or mismatched.
These checks help you detect artifact substitution and unauthorized build paths. They work well alongside source review and malware defenses because an authorized builder can still build a harmful source.
Enforce component policies across teams
Work with your platform engineers to make approved components the default across teams. Curate base images and starter templates so that engineers use pre-hardened and regularly updated images. We recommend maintaining an internal repository of approved libraries and versions to reduce the risk of engineers pulling in something dangerous.
Then, confirm that restrictions actually block the intended requests. For example, Chainguard Libraries policies distinguish Preview mode, which records what would be blocked while installs succeed, from Enforce mode, which blocks policy violations.
After verifying the active policy, test a fresh retrieval of a harmless package deliberately denied by policy, and confirm that the intended policy rejects it. If it succeeds instead, resolve the mode, routing, or exception that allowed it before counting that path as protected.
Patch continuously
Maintain an up-to-date inventory of your third-party components and track their versions. Prioritize patches by exploit activity, how exposed the component is, and what it can access in your systems. Automate update proposals and tests, then rebuild and redeploy the affected software.
Make sure your cooldowns have a controlled exceptions process for any urgent patches. When waiting would leave an actively exploited vulnerability exposed, have security review the specific replacement version and approve a narrow exception. Record why the exception was needed and when it will be reviewed.
Unfortunately, patching alone isn’t enough. Vet the reputation and activity of all open source projects you depend on. If a critical library is no longer maintained, plan a supported replacement or take explicit responsibility for maintaining a fork.
Monitor changes and prepare to respond
Monitor authentication attempts, configuration changes, and release activity across source control, build systems, repositories, and production. Assign responders to alerts about unexpected publishers, workflow changes, or deployments outside the approved path.
When a compromise is suspected, the first step is to identify affected artifacts and deployments. Then, block further use of suspect artifacts, isolate affected systems, and revoke potentially exposed credentials. Preserve logs for investigation, then rebuild from reviewed source in a clean environment and verify the replacement before redeploying. Your prevention program should reduce the chance and reach of an incident while keeping this response work ready.
Frameworks and standards that protect software supply chains
Putting these controls into practice across teams requires shared expectations for how software is developed, built, and verified. Use frameworks, standards, and tools to help you assess gaps in your processes, set requirements for suppliers, and evaluate the evidence behind their security claims.
SLSA
Supply-chain Levels for Software Artifacts (SLSA) is a framework for improving software supply chain integrity. Version 1.2 separates Source and Build tracks, so source-control practices and build protections have their own requirements.
The Build track progresses from provenance at level 1, through signed provenance from a hosted build platform at level 2, to a hardened build platform at level 3. (Though it’s important to note that a build-track assurance doesn’t necessarily establish that the source itself is free of malicious code.) When assessing a supplier's claim, check the relevant track and level to understand which protections it covers.
NIST’s Secure Software Development Framework (SSDF)
NIST’s Secure Software Development Framework (SSDF), published as SP 800-218, provides secure development practices you can integrate into your SDLC. The SSDF organizes practices into four groups: Prepare the Organization, Protect Software, Produce Well-Secured Software, and Respond to Vulnerabilities. Use these groups to connect prevention with vulnerability response and the organizational responsibilities that sustain both.
OWASP SCVS
Open Worldwide Application Security Project (OWASP) Software Component Verification Standard (SCVS) is a community-driven standard with activities, controls, and best practices for handling software components securely. SCVS serves as a maturity model or checklist for software component due diligence. Use SCVS to assess your component controls, evaluate supplier evidence, and prioritize improvements.
How Chainguard supports supply chain prevention
Chainguard Libraries provides packages rebuilt from upstream source in the Chainguard Factory, reducing reliance on public registries. Chainguard-built packages include verifiable provenance and SBOMs. Optional upstream fallback serves eligible packages Chainguard hasn’t built, subject to malware and greyware scanning and configured protections such as cooldowns.
Chainguard Containers supplies minimal, hardened container images with ongoing updates, signatures, provenance, and SBOMs. These allow your engineers to start from a maintained base while keeping application testing, deployment, and release verification in their own workflow.
Use these components where they close gaps in your dependency and container paths, alongside the access and build controls that protect your own software. To discuss how they fit your environment, request a demo.
Frequently Asked Questions
Related articles