Most supply chain security programmes I have reviewed produce an SBOM, store it in a bucket, and stop. That is a document, not a control. It tells an auditor what you believed was in an artifact; it does not stop a malicious dependency from being built into the image, and it does not stop an unauthorised image from running.
The attacks that matter are well documented and mostly the same shape: compromise a maintainer account or a build system, publish a version that behaves normally in CI, and wait for it to be pulled into thousands of pipelines. Your defence has to interrupt that chain at a point where a machine, not a human, is checking.
Provenance is the control, the SBOM is the inventory
Signed build provenance answers a question an SBOM cannot: was this artifact produced by the pipeline I expect, from the source I expect, without a human able to inject steps? A SLSA-style attestation binds the artifact digest to the source repository, the commit, the builder identity and the build parameters, signed by the build platform rather than asserted by the build script.
The SBOM remains worth generating, for one concrete reason: when the next widely-used library has an emergency disclosure, the difference between answering 'are we affected' in ten minutes and two days is whether you can query artifacts by component. Generate it at build time from the actual build, store it as an attestation next to the image, and make it queryable. Anything else is a file nobody opens.
# Verification has to live where code cannot skip it: the admission controller.
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-provenance
spec:
images:
- glob: "registry.internal/**"
authorities:
- keyless:
url: https://fulcio.sigstore.dev
identities:
# Only our own release workflow, on the main branch, can produce
# an image this cluster will run.
- issuer: https://token.actions.githubusercontent.com
subject: https://github.com/acme/platform/.github/workflows/release.yml@refs/heads/main
attestations:
- name: must-have-provenance
predicateType: https://slsa.dev/provenance/v1That policy is the moment the programme stops being paperwork. An image built on a laptop, or by a fork, or by a workflow someone added to a feature branch, does not start. Signing without enforced verification is a signature nobody checks — which is to say, decoration.
Pin, review, and shrink
- Pin dependencies by digest, including base images and CI actions. A mutable tag is a remote code execution waiting for a maintainer to have a bad week.
- Use a lockfile with hashes and make CI install from it strictly; unhashed resolution at build time is where substitution attacks land.
- Run a private registry mirror so a deleted or hijacked upstream package cannot change what your builds get.
- Require review for new dependencies, not just new code. Adding a package is adding a maintainer to your trust boundary.
- Shrink the runtime image to distroless or scratch; every removed binary is a tool an attacker does not inherit.
Signing without enforced verification is a sticker on a box nobody opens.
The build system deserves the same scrutiny as production, because it has production's credentials and far less monitoring. Ephemeral runners, no long-lived cloud keys, OIDC federation for deploys, and restricted permissions per workflow. In practice the most common real-world compromise path is not an exotic dependency attack — it is a CI token with broad rights, sitting in a repository setting, valid indefinitely, that someone extracted through a pull request from a fork.