Research & Analysis · Application security · 17 min read

How Do You Verify a Container Image's Build Attestation?

Sources checked against the cited passages; awaiting my final read before this is indexed.

Verify the image you will deploy, not just the signature you received. A deployment gate should authenticate the attestation, match its subject to the artifact's digest, recognize the statement and predicate types, and compare the builder, source and build inputs against trusted expectations. It must then establish that this artifact is authorized for this environment under the current release policy. A valid build statement alone does not grant that permission.

I'd also require the deployment to use the exact artifact that passed. If verification examines one digest and execution later resolves a mutable name to another, the successful check no longer answers the operational question. The same problem appears less obviously when a gate verifies a multi-platform image index but its policy says nothing about the platform-specific image that will run.

This article develops a proposed consumer-side acceptance method using original specifications, NIST guidance and supply-chain research. It doesn't report a tested pipeline, certify a product or claim that provenance proves software harmless. Its purpose is to make a release decision explainable, including the circumstances in which good cryptographic evidence should still produce a denial.

Separate an authentic statement from an authorized release

A build attestation supports a bounded proposition: an identified party makes specified claims about particular artifacts. That proposition differs from “the software is secure,” “the source change was approved,” or “this version may run in production today.” You need to know which of those claims the evidence actually addresses before building policy around it.

NIST SP 800-204D describes CI/CD as stages and handoffs across a software supply chain, with distinct actors and granular authorizations (Chandramouli et al., 2024). That is the useful unit of analysis. A builder may be trusted to report what it built without being authorized to choose the release destination. An artifact repository may distribute evidence without becoming the authority that decides whether its contents are acceptable.

I would give the gate an explicit acceptance contract. It names the permitted artifact class, evidence types, trusted issuers and builders, source and parameter constraints, target environment and release authority. The contract belongs to the consumer's trusted configuration. An incoming attestation cannot nominate a new trusted signer or relax the conditions used to evaluate itself.

A useful conceptual model is a conjunction: artifact binding, authenticated evidence, acceptable build, authorized release and execution of the approved digest must all hold. These are conditions, not points in a maturity score. An unknown result in a required condition cannot be averaged away by several successful checks elsewhere. Nor does failure necessarily mean malicious activity; it means the proposed deployment has not met its stated acceptance conditions.

This distinction improves diagnosis. “Signature invalid,” “builder not authorized,” “wrong source,” and “release approval expired” call for different responses. Collapsing them into a generic verification error makes pressure to bypass the gate more likely, because engineers cannot see whether they have a packaging problem, a trust-policy problem or an incident.

Bind the evidence to bytes, then follow the image graph

The in-toto Statement specification binds a predicate to subjects identified by digests. It explicitly makes digest matching central; a subject's name has contextual semantics agreed by producer and consumer (in-toto Project, n.d.). A familiar repository name or a convincing release tag therefore cannot substitute for the digest relationship.

OCI content descriptors identify referenced content through properties including its digest, size and media type. The specification calls for checking retrieved content against the digest when it comes through untrusted sources and warns against trusting content whose length disagrees with the descriptor (Open Container Initiative [OCI], n.d.-a). Your gate should use a verifier that understands the artifact format rather than treating any matching hash-shaped string as sufficient.

For a simple image, record the exact manifest digest evaluated. For an image index, the graph matters. The OCI index specification defines an index that refers to manifests, potentially for different platforms, using descriptors and platform information (OCI, n.d.-b). The index digest and a child manifest digest identify different objects. A statement about one should not silently be treated as a statement about the other.

That does not mean signing an index provides no integrity for its referenced descriptors. It means you must define the claim's scope. Does the accepted build provenance describe production of the whole index and the included images? Does your policy require separate provenance for the platform-specific child? Which platforms may be selected? Those are consumer expectations that the format alone cannot decide.

I'd keep the approved index digest, selected platform and resolved manifest digest together in the deployment decision. If your policy permits any member of an approved index, make that explicit. If it requires every runnable platform image to satisfy separate conditions, enforce those conditions before admitting the index. Otherwise a test performed on one architecture can be mistaken for approval of every architecture the registry offers.

Verify the envelope without changing what was verified

An attestation normally has several interpretation layers. A signed envelope carries bytes; those bytes encode a statement; the statement names a predicate type; and the predicate supplies the claim. Each layer has a different job. A signature check cannot tell you whether your policy interpreted the right claim in the right way.

The DSSE protocol authenticates both the payload type and serialized body through its pre-authentication encoding. It also says that the application must receive the same serialized body that was verified, rather than reparsing the envelope to extract a potentially different payload (Secure Systems Lab, 2024). This is an important engineering boundary: the decision must concern the authenticated object, not a later representation selected by another parser.

DSSE's optional keyid is an unauthenticated hint for narrowing candidate keys, not an authorization fact (Secure Systems Lab, 2024). I would review implementations that accept a trusted-looking key identifier without validating the corresponding signature against configured trust. A hint supplied alongside the evidence cannot establish the evidence's authority.

Next, require the statement type and predicate type your policy understands. For example, the in-toto Statement v1 identifier and the SLSA provenance v1 predicate identifier describe different layers. An SBOM or vulnerability-scan predicate does not become build provenance because it arrived in the same envelope format. Unsupported required evidence should remain unsupported, not be counted as a successful generic attestation check.

I wouldn't implement these parsers casually in deployment glue code. Use maintained verification components, constrain accepted formats and retain the relevant version/configuration in the decision record. The proposed review here is architectural; it is not a substitute for testing the actual implementation's parsing, signature handling and integration behavior against its supported formats.

Authenticate the signer and authorize the builder separately

Sigstore's verification documentation illustrates identity-based verification using both a certificate identity and an OpenID Connect issuer. It also distinguishes signature verification from claim checking; disabling claim checking leaves some assertions unchecked even when the signature verifies (Sigstore, n.d.-b). A green command result is only meaningful when you know which checks the invocation performed.

For your environment, decide which issuer-and-identity combinations may make which statements. An identity issued by a recognized identity provider is not automatically an approved production builder. A broad identity pattern can also accept more workflows or repositories than its author intended. I'd review the complete matching rule, including the behavior for unexpected identities, rather than inspect a sample that happens to match.

SLSA's artifact-verification guidance defines roots of trust using recognized signing identities and builder identifiers, with the maximum build level the consumer accepts for each (SLSA, n.d.-c). That makes an important distinction: the producer's claimed level does not determine the verifier's trust. The consumer can recognize an identity while assigning a more limited assurance level.

The build platform's protections then determine what that trust can reasonably mean. SLSA's Build L3 requirements protect provenance generation from tenant-controlled build steps and restrict access to signing material; they also require isolation between builds (SLSA, n.d.-b). A workflow that can write arbitrary provenance fields and sign them with a freely available secret does not gain equivalent assurance merely by emitting the same schema.

I would document the platform boundary and the actors who can change it. Builder administration, workflow configuration, signing configuration and consumer policy are related authorities. If one compromised account can alter all four, a valid signature may faithfully authenticate the attacker's chosen story. That is a trust-design limitation, not a cryptographic failure. Strong evidence depends on deciding which systems and administrators you are actually willing to trust.

Compare build semantics, not just recognizable field names

The build's identity is only part of the answer. A trusted service can execute an unauthorized build request. You need expectations about the source and the meaning of its parameters, not merely evidence that the service ran.

SLSA provenance distinguishes externalParameters, supplied through the build's external interface, from platform-controlled internalParameters. It describes resolvedDependencies as the dependencies known to the build, which can include the concrete source revision resolved from an input reference (SLSA, n.d.-a). The distinction helps prevent a reference such as a branch name from being mistaken for the immutable source revision actually used.

For a release, I'd compare the canonical repository and concrete revision with the trusted release authorization. Then inspect the build type and its parameter schema. A parameter that selects an alternate build recipe, dependency source or output mode can change the result while leaving the repository name unchanged. Checking only the repository would miss that difference.

There is a subtle parsing-policy distinction here. SLSA provenance generally allows consumers to ignore unfamiliar extension fields for compatibility, while its verification guidance recommends rejecting unrecognized external parameters because they can alter the requested build (SLSA, n.d.-a, n.d.-c). These are different namespaces with different meanings. “Ignore unknown fields” is not a safe blanket rule for every nested object, and “reject every extension” is not a faithful implementation of extensibility either.

Your policy needs a known build type with understood input semantics. If the parameters are an arbitrary shell program, maintaining an accurate allowlist may be impractical. A controlled recipe identified by an approved revision can make the expectation more manageable, provided you also govern changes to that recipe. This is a design choice to review with the producer, not an excuse to stop inspecting inputs.

Missing dependency detail is another boundary. The specified build requirements treat resolved-dependency completeness as best effort (SLSA, n.d.-b). That cannot prove a complete dependency closure. Don't infer that an absent entry means a component wasn't used. If complete dependency evidence is necessary for the release decision, obtain a mechanism that provides it or record that the requirement remains unsatisfied. Build provenance and the separate questions an SBOM can answer should support each other without being conflated.

Connect the steps without overstating the research

The original in-toto research distinguishes supply-chain layout integrity, artifact-flow integrity and authentication of the parties performing steps (Torres-Arias et al., 2019). Those distinctions help explain why a final binary signature alone cannot establish the integrity of everything that preceded it. A workflow can have authenticated outputs yet lack evidence connecting one step's products to the next step's inputs.

The paper's threat model and evaluation matter as much as its design. Its analysis assumes a trust arrangement involving project-owner keys and examines compromise scenarios; its evaluation includes deployment overhead and retrospective assessment of historical incidents under described configurations (Torres-Arias et al., 2019). That is not an empirical estimate of how much safer your pipeline becomes by enabling one attestation feature.

I would apply the underlying distinctions to the actual release chain. If testing occurs before packaging, what establishes that the tested artifact is the packaged artifact, or that an approved transformation connects them? If promotion rebuilds an image, the rebuilt digest needs evidence of its own. If promotion merely moves identical bytes, preserve the digest relationship and evaluate the destination authorization separately.

This also exposes the limit of accumulating signatures. Several statements signed by the same compromised control plane do not necessarily provide independent corroboration. Two approval steps performed by effectively the same authority may satisfy a superficial count while leaving one point of failure. Independence is an architectural property of the actors, credentials and controls, not a number inferred from the signature list.

The consumer need not reconstruct every compiler dependency before every deployment. It does need an explicit stopping point: which upstream assurances are accepted, which dependencies remain outside the verified boundary, and why that is proportionate to the release. Without that boundary, “verified supply chain” becomes a much larger claim than the evidence supports.

Make missing evidence fail intentionally

An attacker or ordinary storage failure can remove evidence without altering the artifact. Sigstore's attestation guidance explicitly warns that signatures do not guarantee delivery of the signed statement and discusses designing verification to fail closed when required attestations are absent (Sigstore, n.d.-a). A gate that checks provenance only when it happens to find it creates a different policy from one that requires provenance.

For production, I'd distinguish three results internally: satisfied, contradicted and unavailable. The last two may both stop an ordinary deployment, but they should not produce the same diagnostic. An unsupported predicate, a missing bundle and a recognized builder producing an unauthorized source revision are different situations. Preserving that distinction helps responders recover service without weakening unrelated controls.

You also need a defined policy for multiple attestations. Is one matching provenance statement sufficient? Must a release statement and a test statement both be present? Can an unrelated invalid statement cause denial of an otherwise authorized release? Decide which evidence set is authoritative for each requirement. Otherwise “any attestation passed” can accidentally become the acceptance rule, or irrelevant extra evidence can create unnecessary denial of service.

An outage path should be explicit. Retrying a temporarily unavailable evidence service may be reasonable; silently skipping required verification is not the same action. An exceptional deployment can be authorized through a separate, narrow process with its own accountable decision. It should identify the artifact digest, environment, omitted requirement and expiry or review condition. Don't label that exception as a normal provenance pass.

The operational goal is an intelligible failure that engineers can resolve. Give them the policy version, failing condition and safe next step without exposing unnecessary internal credentials or sensitive build metadata. A gate that only says “computer says no” will generate pressure for a permanent bypass, even when the underlying security requirement is sound.

Treat old valid evidence as a separate release decision

Cryptographic authenticity does not establish that an old release remains acceptable. You may have valid evidence for a vulnerable version, a retired build environment or a source revision that was approved only for testing. The current deployment needs current authorization, even when the historical statement remains an accurate record.

The Update Framework separates signature checks from defenses against rollback and freeze attacks. Its client workflow checks metadata versions and expiration times, using previously trusted state and an update-start time (The Update Framework, 2026). The useful lesson is that freshness and version progression require policy and state. Adding a signature does not automatically supply either.

A deployment gate is not necessarily a TUF client, so I'd avoid copying those rules without adapting their purpose. Production rollback can be a legitimate recovery action. The gate needs to distinguish an expressly authorized rollback to a known digest from an adversary's replay of old evidence. A blanket “newer only” rule could obstruct recovery; accepting any historically signed image could reintroduce a known exposure.

I would bind release approval to the artifact, environment and decision interval, while retaining the reason for any rollback. Where a build platform or signing identity is later distrusted, reassess the affected release set using your compromise policy. Don't assume that certificate dates alone answer whether an artifact should be deployed now, or that deleting an identity from a current configuration explains every historical decision.

Evidence retention supports this work. Keep the accepted statement, relevant verification material, trust configuration and policy version with the deployment record. Protect that record from the same actors who could alter the release. It should explain what was accepted at the time and allow a later reviewer to distinguish a changed policy from changed evidence.

Test the boundary with a fictional release matrix

Consider a fictional service published as a multi-platform container image. The production policy recognizes one builder identity and issuer, an approved repository and source revision, a controlled build type, permitted parameters and a separate production release approval. It requires provenance for each runnable platform image admitted through the index. These are illustrative policy choices, not requirements imposed universally by OCI or SLSA.

The following matrix gives expected decisions under that policy. None of these cases was executed here, and no pass rate is inferred.

Fictional variationExpected decision and reason
Approved digest, complete evidence, expected build and current production approvalAdmit that exact digest under the stated policy
Valid signature but subject digest differs from the selected artifactDeny; the evidence does not bind to the artifact
Expected builder and source, but an unrecognized external parameterDeny; the requested build is outside understood expectations
Authentic provenance from an unapproved builderDeny; authenticity does not establish builder authority
Required provenance is absent while the registry is reachableDeny ordinary deployment; investigate evidence availability
Approved index contains a runnable child lacking the required child provenanceDeny under this policy's per-platform evidence requirement
Historically approved image is replayed without current release authorizationDeny ordinary deployment; evaluate any rollback request separately
Verification passes for one digest but the deployment requests anotherDeny; the execution request no longer matches the decision

For each negative case, preserve enough valid surrounding evidence that the intended condition is what fails. A corrupt JSON file is not a meaningful test of source-revision policy because parsing can reject it first. Similarly, an invalid signature tells you nothing about whether the system would reject a correctly signed but unauthorized build parameter.

The positive case matters too. A gate that denies everything can appear strong in a negative-only demonstration while being unusable. Include an authorized release and the intended exceptional recovery path in the real acceptance exercise. Evaluate the actual result at deployment, not merely a verifier's exit status in an isolated test job.

I would also inspect who can bypass the path. A manual registry pull, a separate deployment credential or an administrative override can sit outside the ordinary admission flow. The matrix establishes behavior for the tested path only. Coverage requires examining the other routes by which executable artifacts reach the environment.

Preserve the decision across admission and execution

Verification and deployment may be performed by different components at different times. The handoff must carry the approved digest and its scope, not merely the word “passed.” If the deployment resolves a tag again, a later registry change can disconnect execution from the evidence already evaluated. Pinning the approved content reference and checking the actual execution record addresses that particular gap.

Caching introduces another scope question. A cached decision about a digest under yesterday's policy may not remain valid after the builder is distrusted, the release approval is withdrawn or the target environment changes. I'd key cached authorization to the material decision inputs and define invalidation. You can reuse immutable evidence without treating every previous authorization result as permanent.

There is a performance tradeoff. Repeating expensive work on every admission can make outages and latency operationally significant. Pre-verification can be useful if the receiving gate authenticates its result, understands the policy and trust versions behind it, and checks that the result still applies. The architecture should say which checks are delegated and which remain at the final consumer.

The deployment record closes the loop: artifact digest, selected platform where relevant, accepted evidence, policy/trust versions, authorization result, exception if any and actual destination. Compare that record with the running inventory. This does not prove that runtime behavior is safe, but it makes the narrower assertion—this environment received the artifact that met this policy—investigable.

When the record and runtime differ, don't repair the audit trail by changing the expected digest to match what happened. Treat the mismatch as a failed control or an explicitly documented exceptional action. The point of the record is to preserve the decision boundary, not make deployment dashboards look consistent after the fact.

What this gate cannot promise

The strongest counterargument is that an authorized build can faithfully compile malicious or vulnerable source. That is correct. A build-provenance gate addresses particular integrity and authorization questions; it does not replace source review, vulnerability assessment, secrets protection or runtime controls. Your assurance statement should identify those remaining dependencies instead of claiming complete software security.

There are also limits to organizational trust. If the consumer's root policy is compromised, attackers may authorize their own evidence. If an accepted build platform lies within the very boundary you assumed trustworthy, downstream signature verification may not expose the lie. Independent review, separation of authority and stronger platform controls can reduce particular risks, but this article does not establish a universal protection level for those arrangements.

Legacy suppliers may not provide the required evidence. A risk-based exception or restricted deployment can be appropriate, but absence should remain visible. Don't count a supplier questionnaire as equivalent to cryptographically bound build provenance merely to improve a coverage percentage. Different evidence can support different decisions without being interchangeable.

My starting point would be one real release: trace the image from its source and build evidence through approval to the exact digest running in production. Then challenge the gate with a correctly signed artifact that should be refused. Request a software release-gate assessment to examine the trust boundaries, required denials and recovery path before you rely on an attestation badge as release assurance.

References