Pipeline Security Basics
A CI/CD pipeline is, by design, a privileged automation surface: it can read source code, hold deployment credentials, and push artifacts straight to production. That combination makes it an attractive target — compromising a pipeline can be more valuable to an attacker than compromising a single production server, because it provides a repeatable path to inject malicious code into every future release. Pipeline security is the set of practices that reduce this attack surface: controlling what triggers a pipeline, what a running job can access, and verifying that what comes out the other end is exactly what was intended to be built.
Cricket analogy: A CI/CD pipeline holding deployment credentials is like a team's chief selector holding the master key to every future team sheet -- compromising that one person is far more valuable to a rival than bribing a single player, because it lets them shape every future selection, so associations tightly control who can trigger a selection meeting, what the selector can access, and verify every announced XI matches what was actually decided.
Untrusted Triggers and Inputs
Pull requests from external forks are a classic vector: if a pipeline automatically runs with full secrets on a PR-triggered event, an attacker can open a PR whose code simply exfiltrates every secret in scope. Mature configurations separate 'build and test' workflows (which can run on any PR, sandboxed, without secrets) from 'deploy' workflows (which only run after human review and merge, with elevated access). Similarly, dependencies pulled in during a build — third-party actions, npm packages, base images — are effectively code execution inside the pipeline, so pinning them to a specific commit SHA or verified digest (not just a mutable tag) closes off a supply-chain injection path.
Cricket analogy: Letting any outside club send a 'trial player' straight into training with full access to team tactics is a classic vector for a rival to steal signals, so smart academies run open trials in a sandboxed, tactics-free session first, and only give full playbook access after the player is formally signed and reviewed -- similarly, borrowing a rival team's training drills without locking in the exact version risks them quietly changing the drill later.
Isolating Job Execution
Each job should run in an ephemeral, isolated environment — a fresh container or VM that's destroyed after the job finishes — so that state or malware from one build can't persist into the next. Self-hosted runners deserve particular scrutiny: unlike a cloud provider's ephemeral hosted runners, a persistent self-hosted runner that executes arbitrary PR code can become a foothold into internal infrastructure if it isn't properly network-isolated and reset between jobs.
Cricket analogy: Each match should be played on freshly prepared, neutral ground rather than a team's own home pitch that carries over grass length and pitch conditions from the last match they doctored -- a rented, ephemeral venue destroyed and re-laid after each match prevents any lingering advantage or sabotage from persisting into the next game, unlike a team's permanent home ground, which needs particular scrutiny for tampering between matches.
name: PR Build (untrusted, no secrets)
on:
pull_request:
branches: [main]
permissions:
contents: read # least privilege: no write/deploy scopes
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f # pinned to commit SHA
with:
node-version: '20'
- run: npm ci
- run: npm test
# Note: no deploy step, no secrets exposed to this workflowPinning a third-party action or dependency to a mutable tag like @v4 is like trusting a signpost that someone else can quietly repaint overnight; pinning to a commit SHA is like carving the destination into stone — the maintainer can still publish a new version, but your pipeline keeps using exactly the code you reviewed.
Granting a workflow broad, default write permissions (e.g., GITHUB_TOKEN with repo-wide write access) 'just in case' is a common misconfiguration; if that workflow is ever tricked into running attacker-controlled code, the attacker inherits every permission the token has, not just the ones the workflow actually needed.
Verifying What Ships
Beyond preventing injection, pipeline security also covers proving integrity after the fact: signing build artifacts (e.g., with Sigstore/cosign), generating a Software Bill of Materials (SBOM), and recording build provenance (what commit, what runner, what inputs produced this artifact) let downstream consumers verify an artifact wasn't tampered with between build and deployment.
Cricket analogy: Beyond stopping match-fixing before it happens, cricket also proves integrity after the fact: a match official's signature on the scorecard (like signing with Sigstore/cosign) confirms it's authentic, a full list of every player and substitute used (like an SBOM) documents exactly who contributed, and match records noting the ground, umpires, and conditions (build provenance) let historians verify a result wasn't tampered with after the final ball.
- Pipelines hold privileged access, making them a high-value target worth securing deliberately.
- Separate untrusted PR-triggered builds (no secrets) from trusted deploy workflows (post-merge, elevated access).
- Pin third-party actions, packages, and base images to immutable digests/commit SHAs, not mutable tags.
- Run each job in an ephemeral, isolated environment to prevent cross-build contamination.
- Grant workflows only the minimum token permissions they actually need.
- Sign artifacts and record build provenance so tampering can be detected downstream.
Practice what you learned
1. Why are CI/CD pipelines considered a high-value attack target?
2. What is the recommended approach for workflows triggered by pull requests from external forks?
3. Why is pinning a third-party GitHub Action to a commit SHA safer than pinning to a version tag like @v4?
4. What risk do overly broad default token permissions create in a pipeline?
5. What does recording build provenance help downstream consumers verify?
Was this page helpful?
You May Also Like
Managing Secrets in CI/CD
CI/CD pipelines need credentials for external systems, so secrets must be stored encrypted, injected at runtime, scoped narrowly, and never committed to source control or build logs.
Least Privilege for CI/CD Service Accounts
CI/CD service accounts should hold only the exact permissions their pipeline needs, scoped narrowly and time-limited, so a compromised job can't cascade into a broader breach.
Dependency Scanning and SAST
Automated dependency scanning and static application security testing catch known-vulnerable libraries and insecure code patterns before they reach production, directly inside the pipeline.
GitHub Actions Triggers and Events
Learn how GitHub Actions workflows start: the `on:` key, common webhook events, scheduled runs, manual dispatch, and how to filter events by branch, path, or type.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics