Checksums and Integrity for Releases
A release artifact that has been altered without anyone noticing is more dangerous than a build that visibly fails. SHA256 checksums, GPG signatures and automated verification steps in the GitLab pipeline close this silent attack surface for Magento deployments.
Table of Contents
- 1. Why artifact integrity is a real risk
- 2. Checksums: SHA256 and how they are generated
- 3. Generating checksums automatically in the pipeline
- 4. Verifying checksums before deployment
- 5. GPG signing for release packages
- 6. GitLab Release API and checksum attachments
- 7. SLSA and provenance: where does the artifact come from?
- 8. Composer integrity and composer.lock as the foundation
- 9. Comparison: without vs. with integrity assurance
- 10. Summary
- 11. FAQ
1. Why artifact integrity is a real risk
On its way from the pipeline to the production server, a release artifact passes through several stations: GitLab storage, network transfer, temporary directories on the server. At any of these stations, an unnoticed change can theoretically occur, whether through errors, supply chain attacks, or a faulty transfer process. A corrupted or tampered artifact that gets deployed without verification is more dangerous than a failed build, because it produces no visible error.
For Magento shops, this risk is concrete: vendor/ contains PHP libraries that run directly in production. If a library was altered between build and deploy, that malicious code executes with every request. Supply chain attacks against npm and Composer packages are no longer theoretical, they happen in reality. The countermeasure is not paranoia but a simple technical control: SHA256 checksums that are generated in the build job and verified before deployment.
2. Checksums: SHA256 and how they are generated
A checksum is a cryptographic hash of a file that changes even if only a single byte of the file changes. SHA256 is the current standard: MD5 and SHA1 are no longer sufficient for integrity checks because collisions are theoretically possible. A SHA256 hash is 64 characters long and unique to the exact contents of the file. On Linux systems sha256sum is used, on macOS shasum -a 256.
For a release artifact, the hash is calculated right after creation and stored in a separate file. The convention is a .sha256 file next to the artifact: release-v1.2.3.tar.gz and release-v1.2.3.tar.gz.sha256. Both files are stored as GitLab artifacts. Before deployment, the deployment job reads the .sha256 file, calculates the hash of the actual artifact, and compares the two values. If they match, integrity is confirmed. If they do not match, the pipeline aborts immediately.
package:release:
stage: package
dependencies:
- build:magento
- build:frontend
script:
# Create timestamped release archive
- export RELEASE_TAG="${CI_COMMIT_TAG:-${CI_COMMIT_SHORT_SHA}}"
- tar -czf "release-${RELEASE_TAG}.tar.gz" vendor/ generated/ app/ pub/static/
# Generate SHA256 checksum, stored alongside the archive
- sha256sum "release-${RELEASE_TAG}.tar.gz" > "release-${RELEASE_TAG}.tar.gz.sha256"
# Display checksum for pipeline log transparency
- cat "release-${RELEASE_TAG}.tar.gz.sha256"
- echo "RELEASE_TAG=${RELEASE_TAG}" >> release.env
artifacts:
name: "release-${CI_COMMIT_SHORT_SHA}"
paths:
- release-*.tar.gz
- release-*.tar.gz.sha256
- release.env
reports:
dotenv: release.env
expire_in: 14 days
3. Generating checksums automatically in the pipeline
The checksum step must be part of the package job, not a step added afterward. Only when the hash and the artifact are produced in the same job is it guaranteed that the hash actually describes the artifact that was built, and not a later, possibly altered version. The checksum file is listed alongside the artifact in artifacts.paths and therefore shares its lifecycle.
For multiple artifacts, a shared SHA256SUMS file following Linux convention is recommended: all hashes in one file, one hash per line, with the file name. This file can be verified with a single command: sha256sum -c SHA256SUMS. This checks all artifacts at once and outputs either OK or an error for each file. In GitLab CI, an error at this step automatically causes a job failure and stops the pipeline, which is exactly the desired behavior.
4. Verifying checksums before deployment
Verification must happen as an explicit step in the deploy job or as a separate verify job before deployment. The flow is always the same: download the artifact and the checksum file, calculate the hash of the artifact, compare it with the stored hash. Only on a match does the deployment continue. This step takes seconds but prevents corrupted or tampered packages from being deployed.
An important point: the checksum file itself must also be protected. If an attacker can replace both the artifact and the checksum file, the protection becomes worthless. This is where GPG signing comes in: the checksum file is signed with a private key known only to the CI system. Before deployment, the signature is verified with the public key. This way, no unnoticed manipulation can take place, neither on the artifact nor on the checksum.
5. GPG signing for release packages
GPG (GNU Privacy Guard) enables cryptographic signatures that prove an artifact originates from a specific source and has not been changed since signing. In a GitLab CI context, a GPG key pair is generated, the private key is stored as a protected CI/CD variable, and the public key is made available to all deployments. The build job signs the release package, the deploy job verifies the signature before unpacking it.
The setup requires a one-time effort: generate the key pair, store the private key base64-encoded as a GitLab variable, import the public key on the deployment server. In day-to-day operation, signing and verification happen fully automatically. For Magento shops that operate in regulated environments or have specific compliance requirements, GPG signing is not an optional measure but a documented security feature of the deployment process.
sign:release:
stage: package
dependencies:
- package:release
script:
# Import GPG private key from CI variable (base64-encoded)
- echo "${GPG_PRIVATE_KEY}" | base64 -d | gpg --batch --import
# Sign the release archive, creates .asc detached signature
- gpg --batch --yes --armor --detach-sign
--local-user "${GPG_KEY_ID}"
release-*.tar.gz
# Verify signature immediately to confirm signing worked
- gpg --verify release-*.tar.gz.asc release-*.tar.gz
artifacts:
paths:
- release-*.tar.gz.asc
expire_in: 14 days
verify:signature:
stage: verify
dependencies:
- package:release
- sign:release
script:
# Import public key on verification side
- echo "${GPG_PUBLIC_KEY}" | gpg --batch --import
# Verify GPG signature before any deployment step
- gpg --verify release-*.tar.gz.asc release-*.tar.gz
- echo "Signature verified, safe to deploy"
6. GitLab Release API and checksum attachments
GitLab offers a Release API that can automatically create a release entry with attached assets for every Git tag. Checksum files can be attached as release assets and stay permanently linked to the tag, even after the CI artifact has expired. This is especially useful for long-term traceability: which version was deployed, and what is the hash of the deployed package?
The GitLab Release CLI or the API makes it possible to attach checksum files as links. In the simplest case, the SHA256SUMS file is stored in S3-compatible object storage and the URL is attached to the GitLab release. For internal deployments without an external storage solution, it is enough to attach the checksum file as a downloadable asset on the GitLab release. What matters is the consequence: every tagged release has a documented, verifiable checksum.
7. SLSA and provenance: where does the artifact come from?
SLSA (Supply chain Levels for Software Artifacts) is a framework that describes the conditions under which an artifact was built. The core idea is provenance: a machine-readable document that records which commit served as the input, which pipeline ran the build, and which runner was used. GitLab supports provenance generation through the generate_provenance mechanism for releases.
For Magento deployments, reaching SLSA Level 1 only requires documenting the build environment: Git commit SHA, pipeline ID, runner ID, timestamp and artifact hash. This information can be generated as a JSON file in the package job and stored as an artifact. This makes every production state traceable back to an exact Git commit, a basic prerequisite for incident response and compliance evidence in regulated industries.
8. Composer integrity and composer.lock as the foundation
The foundation of all PHP-side integrity checks is the composer.lock file. For every package it fixes the exact version and the SHA hash of the downloaded file. composer install with a valid composer.lock automatically verifies that the installed packages exactly match the expected hashes. This protects against compromised package mirrors and downgrade attacks on Composer packages.
In the GitLab CI build job, composer.lock must therefore always be committed, and composer install (not composer update) must be used. An additional check: composer validate --strict checks whether composer.json and composer.lock are consistent. Using composer install --no-dev in the build job ensures that no development dependencies end up in the production artifact, eliminating yet another attack surface.
9. Comparison: without vs. with integrity assurance
The following table shows which risks exist without integrity checks and how each measure addresses them.
| Measure | Without measure | With measure | Effort |
|---|---|---|---|
| SHA256 checksum | Transfer errors go unnoticed | Corruption is caught before deployment | Minimal (1 line) |
| GPG signature | Tampering in storage is possible | Origin is cryptographically proven | Medium (key setup) |
| composer.lock | Package versions drift | Fixed, verified package hashes | Minimal (always commit) |
| Provenance document | No proof of build origin | Full traceability | Medium (JSON generation) |
| Verify job in the pipeline | Deployment runs without checks | Pipeline stops on mismatch | Minimal (separate job) |
The table makes it clear: the first three measures, SHA256 checksums, committing composer.lock, and a verify job, require minimal effort and cover the most common risks. GPG signing and provenance documents are useful for higher security requirements or regulated environments, but they are not a basic requirement for a secure deployment process.
10. Summary
Artifact signing, checksums and integrity checks are not exaggerated security measures, but the logical consequence of the fact that release artifacts flow through external systems. SHA256 checksums are generated in the package job and verified before deployment. GPG signatures cryptographically prove the origin of the package. The composer.lock file ensures that all PHP dependencies are installed with fixed versions and verified hashes. Provenance documents provide traceability for compliance and incident response.
The overall effort for basic integrity assurance is small: one additional checksum step in the package job and one verify job before deployment. These two additions turn a deployment process that relies on hope into one that relies on verification. For Magento shops handling customer data and payment processing, that difference is not trivial.
Artifact Integrity for Magento: The Essentials at a Glance
SHA256 in the package job
sha256sum release.tar.gz > release.tar.gz.sha256 right after archive creation. Store both files as artifacts.
Verify before deploy
sha256sum -c release.tar.gz.sha256 as a required step before every deployment. Pipeline fails on mismatch.
Commit composer.lock
Never exclude composer.lock in .gitignore. Always composer install, never composer update in the build job.
GPG for higher requirements
A detached signature (.asc) next to the artifact cryptographically secures its origin. One-time setup, fully automatic afterward.
11. FAQ: Artifact Signing and Checksums in GitLab
1Why is MD5 no longer enough?
2How to keep a GPG key secure in GitLab?
3GPG, or are checksums enough?
4Is SLSA needed for Magento?
5Does composer.lock protect against all attacks?
6How long to keep a checksum file?
7What happens on a failed checksum?
8Create a checksum after the fact?
9Verify an artifact on the production server?
sha256sum -c release.tar.gz.sha256 after the transfer. On mismatch, abort immediately and trigger a new build.