Securing the software supply chain: SBOMs, provenance, and signing
20 June 2026 · 8 min read · Nintech Security
The average production service is a thin layer of first-party code on top of hundreds of open-source dependencies, built by a CI pipeline with more privileges than most employees, and shipped through registries that anyone can publish to. Attackers noticed. Compromising one popular package, one build system, or one maintainer account beats phishing targets one at a time, and the last few years have supplied working demonstrations at every layer. The response — SBOMs, provenance, signing — is not compliance theatre when done properly; it is the difference between answering 'are we affected?' in twenty minutes or three weeks.
Your dependencies are the attack surface now
A typical Node or Python service pulls in several hundred transitive dependencies; a typical container image adds an operating system's worth of packages underneath. Each one is code you execute with production privileges, written by someone you have never met, delivered through infrastructure you do not control. The attack patterns are well catalogued by now: typosquatted package names, dependency confusion (publishing a public package that shadows a private internal name — Alex Birsan's 2021 research breached Apple, Microsoft, and Tesla with this alone), hijacked maintainer accounts pushing malicious patch releases, and install-time scripts that exfiltrate CI credentials the moment npm install runs.
The economics favour the attacker: one compromised package with a million weekly downloads is a million potential footholds, and most victims will not notice because the package still does what it says on the tin. Meanwhile the defenders' traditional tooling — perimeter firewalls, endpoint agents — sees nothing wrong, because the malicious code arrived through the same trusted pipeline as everything else. Defence has to move into the pipeline itself: know what you ship, verify where it came from, and constrain what the build can do.
An SBOM is only useful if you can query it at 2am
A software bill of materials — in practice a CycloneDX or SPDX document generated at build time by tools like Syft or your package manager's native support — is an inventory of every component and version in an artefact. The test of whether yours is real: when the next Log4Shell lands, can you answer 'which of our deployed services contain log4j-core between 2.0 and 2.14.1, and which environments are they in?' in minutes, from a query, without asking every team to grep their lockfiles? During the actual Log4Shell response in December 2021, most organisations spent the first days — days during which exploitation was rampant — just building the list of affected systems. That is the cost of not having this.
The common failure is generating SBOMs to satisfy a procurement checkbox (or the US Executive Order 14028 requirements flowing down through contracts) and filing them in a bucket nobody queries. The useful pattern is: generate the SBOM in CI for every build, attach it to the artefact, and load it into a queryable store — Dependency-Track is the standard open-source choice — correlated with what is actually deployed where. Two honest caveats: SBOM accuracy is only as good as the generator, and tools disagree at the edges (statically linked binaries, vendored code, dynamically loaded plugins); and an inventory tells you what you have, not whether the vulnerable code path is reachable — expect false positives and build a triage muscle, or the alerts will be ignored within a month.
Provenance and signing: proving the artefact is what the build produced
An SBOM tells you what is inside an artefact; provenance tells you how it came to exist — which source revision, which build system, which parameters — and a signature makes that claim tamper-evident. SLSA (Supply-chain Levels for Software Artifacts) grades this: at the levels worth aiming for, builds run on hosted, hardened platforms and produce signed provenance that a deploy-time policy can verify, so an artefact that did not come from your CI building your repo simply does not get admitted to production. GitHub Actions and GitLab can emit SLSA provenance natively, and Kubernetes admission controllers can enforce verification.
Sigstore made the signing half practical by removing the key-management problem that killed previous attempts: cosign signs artefacts using short-lived certificates bound to a workload or OIDC identity ('this was signed by the release workflow of github.com/yourorg/yourrepo'), with signatures recorded in Rekor, a public transparency log. npm's provenance attestations and PyPI's Trusted Publishers build on the same foundations. The caveat that matters: signing proves origin, not safety. A signed, provenance-attested build of a repo whose source was already backdoored is a beautifully authenticated backdoor. Provenance shrinks the attack surface to 'compromise the source or the build platform' — which is genuine progress — but it does not eliminate it, as the next section shows.
What the real incidents teach, and what to do this quarter
SolarWinds (2020) and xz-utils (2024) are worth studying together because they bracket the problem. SolarWinds' attackers compromised the build environment itself — malicious code was injected during compilation, so the shipped Orion updates were legitimately signed by the vendor and clean in source control. Roughly 18,000 customers installed them. Lesson: the build system is a tier-zero asset; signing without build integrity is a signed lie. The xz backdoor was the opposite end: a patient attacker spent over two years building trust as a volunteer maintainer, then landed an obfuscated backdoor targeting sshd — hidden partly in binary test files, absent from the readable source — in xz 5.6.0/5.6.1 (CVE-2024-3094). It reached Debian and Fedora pre-releases and was caught essentially by luck, when Andres Freund investigated a 500ms SSH slowdown. Lesson: 'it is open source, someone reviewed it' is a hope, not a control, and single-maintainer criticality is a systemic risk no scanner flags.
None of that means paralysis; it means prioritised, boring work. A realistic quarter for one platform team: generate and centrally store SBOMs for every production build and wire vulnerability matching to them (Syft plus Dependency-Track or Grype is an afternoon per pipeline); turn on lockfiles and exact pinning everywhere, with automated update PRs so pinning does not mean stagnation; lock down CI — short-lived OIDC credentials instead of long-lived secrets, and egress restrictions on build runners so a compromised dependency cannot phone home with your tokens; and start signing release artefacts with cosign, with verification enforced at deploy for one critical service as the template. Then, honestly, look at your own xz exposure: which single-maintainer packages sit in your critical path, and does your update policy leave a buffer before adopting fresh releases of low-level libraries? None of this is glamorous. All of it is cheaper than the incident.
Working on something like this? Talk to an engineer.