Skip to content
Français
CI/CD & Automatisation medium

GitHub Actions attestations

30 min de lecture

Read this page in French

Attestations let you prove cryptographically where your artefacts come from: who built them, from which code, with which workflow. They are a key part of supply chain security.

What you will learn

  • Understand attestations and what they prove cryptographically
  • Tell apart a provenance attestation (SLSA) and an SBOM attestation
  • Generate an attestation for an artefact or a Docker image
  • Attest an SBOM alongside the provenance
  • Know where GitHub and Sigstore keep the attestations

What is an attestation?

An attestation is a signed document certifying information about an artefact:

{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "my-app",
"digest": {
"sha256": "abc123..."
}
}
],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://actions.github.io/buildtypes/workflow/v1"
},
"runDetails": {
"builder": {
"id": "https://github.com/actions/runner"
}
}
}
}

What the attestation proves:

InformationValue
ArtefactSHA256 hash of the artefact
BuilderGitHub Actions runner
Repositoryowner/repo
CommitThe commit SHA
WorkflowFile and reference
SignerSigstore (Fulcio/Rekor)

Types of attestation

GitHub Actions produces two standard attestation types, provenance and SBOM, and allows custom attestations.

Provenance attestation (SLSA)

Proves how the artefact was built:

  • From which source code
  • With which workflow
  • With which build parameters

SBOM attestation

Proves what the artefact contains:

  • The list of dependencies
  • The exact versions
  • The licences

Custom attestation

You can add your own information:

  • Test results
  • Security scan results
  • Business metadata

Generating an attestation

The official actions/attest-build-provenance action generates and signs the attestation. The workflow needs three permissions: id-token, contents and attestations.

With actions/attest-build-provenance

This action produces a provenance attestation for any artefact file.

name: Build and Attest
on:
push:
branches: [main]
permissions:
id-token: write # To sign with Sigstore
contents: read
attestations: write # To publish the attestation
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: Check out the code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Build the artefact
run: |
npm ci
npm run build
tar -czf dist.tar.gz dist/
- name: Generate the attestation
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
with:
subject-path: dist.tar.gz

For a Docker image

For an image, the attestation targets the published digest and can be attached directly to the registry.

jobs:
build:
runs-on: ubuntu-24.04
permissions:
id-token: write
contents: read
attestations: write
packages: write
steps:
- name: Check out the code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Log in to GHCR
uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push the image
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Generate the attestation
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
with:
subject-name: ghcr.io/${{ github.repository }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true

SBOM attestation

Generate an SBOM (Software Bill of Materials) then attest it, to prove the exact content of the artefact:

jobs:
sbom:
runs-on: ubuntu-24.04
permissions:
id-token: write
contents: read
attestations: write
steps:
- name: Check out the code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Generate the SBOM
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
id: sbom
with:
format: spdx-json
output-file: sbom.spdx.json
- name: Attest the SBOM
uses: actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e # v4.1.0
with:
subject-path: dist.tar.gz
sbom-path: sbom.spdx.json

Where are the attestations stored?

A generated attestation is kept in two places, which makes it both queryable and tamper-evident.

The GitHub Attestations API

Attestations are stored in GitHub and reachable through:

  • The REST API: GET /repos/{owner}/{repo}/attestations/{digest}
  • The CLI: gh attestation verify

The Sigstore transparency log

Attestations are also recorded in Rekor, the Sigstore transparency log. That guarantees they cannot be altered after the fact.

A complete workflow

Here is a full release workflow: build, SBOM, double attestation (provenance and SBOM), then publication.

name: Build, Attest and Release
on:
push:
tags:
- 'v*'
permissions:
id-token: write
contents: write
attestations: write
packages: write
jobs:
build:
runs-on: ubuntu-24.04
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Check out the code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Build the artefact
id: build
env:
REF_NAME: ${{ github.ref_name }}
run: |
npm ci
npm run build
tar -czf "my-app-$REF_NAME.tar.gz" dist/
echo "digest=$(sha256sum "my-app-$REF_NAME.tar.gz" | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Generate the SBOM
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
format: spdx-json
output-file: sbom.spdx.json
- name: Attest the provenance
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
with:
subject-path: my-app-${{ github.ref_name }}.tar.gz
- name: Attest the SBOM
uses: actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e # v4.1.0
with:
subject-path: my-app-${{ github.ref_name }}.tar.gz
sbom-path: sbom.spdx.json
- name: Create the release
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
files: |
my-app-${{ github.ref_name }}.tar.gz
sbom.spdx.json

Good practices

A few reflexes make sure your attestations genuinely secure the delivery chain.

1. Always attest the releases

Every distributed artefact must carry a provenance attestation; without one, the consumer cannot verify where it came from.

2. Attest the Docker images

For an image, push-to-registry: true attaches the attestation directly to the image in the registry.

- uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
with:
subject-name: ghcr.io/${{ github.repository }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true # Attaches the attestation to the image

3. Generate an SBOM as well

The provenance attestation says how the artefact was built; the SBOM says what it contains. The two are complementary.

4. Verify before deployment

An attestation is only worth something when it is verified on the consumer side. Before deploying an artefact, validate its provenance: see Verifying attestations.

Key points

  • An attestation is a signed document proving the origin of an artefact: source code, workflow, builder.
  • The provenance attestation (SLSA) says how the artefact was built; the SBOM attestation says what it contains.
  • actions/attest-build-provenance and actions/attest-sbom require the id-token: write and attestations: write permissions.
  • Attestations are signed through Sigstore and recorded in the Rekor transparency log, so they cannot be altered afterwards.
  • An attestation only protects you when it is verified before deployment.

For the full reference, see the official documentation on artifact attestations.

Next steps

  • Verifying attestations: the consumer side, the only step that turns an attestation into an actual guarantee.
  • Security checklist: the recap that places attestation among the other controls to tick before publishing.
  • OpenSSF Scorecard: the check that measures whether your releases are signed, among eighteen others.

Is this site useful to you?

Fewer than 1% of readers support this site.

I maintain more than 700 free guides, with no ads and no tracking. Any support, even a symbolic one, helps cover hosting and keeps these resources free. Thank you for the help.

The form does not show? Open Ko-fi in a new tab.

Subscribe and follow my DevSecOps work on LinkedIn