Safeguard
DevSecOps

CI/CD Pipeline Security Best Practices for 2026

Your build pipeline is production-adjacent infrastructure with credentials to everything. Here are the CI/CD security practices — mapped to the OWASP Top 10 CI/CD risks — that actually close the gaps attackers use.

Priya Mehta
DevSecOps Lead
Updated 5 min read

The build pipeline is the most under-defended piece of production infrastructure in most organizations. It holds credentials to your cloud accounts, your package registries, your production deploy keys, and your source control — and it will run, without a second thought, whatever code a pull request tells it to. The 2025 tj-actions/changed-files compromise (CVE-2025-30066) was a clean demonstration: attackers repointed a widely-used action's tags to malicious code that dumped CI runner memory, exfiltrating secrets from an estimated 23,000 repositories that had trusted a mutable version tag. The pipeline didn't get "hacked" in any exotic sense. It did exactly what it was told, with the privileges it had been given. CI/CD pipeline security is mostly about reducing what the pipeline is trusted to do and verifying what it pulls in.

Treat the pipeline as production, not as a script

The mental model that fixes most CI/CD problems is this: your pipeline is production-adjacent infrastructure with standing access to nearly everything you care about, so it deserves the same threat modeling, access control, and monitoring as a production service. The OWASP Top 10 CI/CD Security Risks framework codifies where this breaks down — insufficient flow control, inadequate identity management, dependency-chain abuse, poisoned pipeline execution (PPE), and insufficient credential hygiene are the recurring themes. Nearly every real-world pipeline breach maps to one of them, and nearly every one is preventable with configuration you already have access to.

Lock down pipeline identity and permissions

The default posture for most pipelines is over-privileged. A CI token that can write to any repository, or a long-lived cloud key baked into a secret store, is a single exfiltration away from full compromise. Three practices close most of the gap:

  • Default to read-only tokens. Grant write scope per-job, only where a job genuinely needs it (publishing a package, pushing a tag).
  • Replace static cloud credentials with short-lived OIDC. Federated identity means the pipeline exchanges a signed workflow token for a cloud credential that expires in minutes, so there is no long-lived key to steal.
  • Scope secrets to the jobs that need them. A build job that compiles code has no reason to see your production deploy key.
# GitHub Actions: least-privilege + OIDC, no static cloud keys
permissions:
  contents: read          # read-only by default

jobs:
  deploy:
    permissions:
      id-token: write      # only this job gets to mint an OIDC token
      contents: read
    steps:
      - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3  # pin to SHA
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::111122223333:role/ci-deploy
          aws-region: us-east-1

Prevent poisoned pipeline execution

Poisoned pipeline execution happens when untrusted input — the contents of a fork's pull request — is allowed to run with privileged context. The classic footgun is a workflow triggered by pull_request_target that checks out and executes PR code; because that trigger runs with the base repository's secrets, an attacker's fork can steal them. The rule is absolute: never check out and run untrusted PR code in a context that holds secrets. Require manual approval before workflows run for first-time contributors, and keep secret-bearing jobs on trusted triggers only.

Secure the software supply chain into the pipeline

Everything the pipeline pulls in is code you're about to run with production credentials. Two controls matter most. First, pin third-party dependencies — actions, base images, packages — to immutable references (commit SHAs and content digests), never mutable tags like @v4 or :latest. Second, generate a software bill of materials for every artifact and scan it with software composition analysis as a pipeline stage, so a known-vulnerable or malicious dependency is caught before it's baked into an image. Fail the build on reachable, high-severity findings rather than on raw counts, so the gate stays trustworthy.

Sign and verify build artifacts

The final gap most pipelines leave open is provenance: nothing proves that the container image in your registry was actually built by your pipeline from your source. Artifact signing (with a tool like Sigstore's cosign) and generating SLSA provenance attestations close this. Your deploy step then verifies the signature before it runs anything, so an attacker who pushes a rogue image to the registry can't get it deployed. This is the difference between "we hope this artifact is ours" and "we cryptographically require it to be."

CI/CD pipeline security checklist

Risk areaControlVerify by
Over-privileged tokensRead-only default, per-job writeAuditing permissions: blocks
Static cloud secretsShort-lived OIDC federationNo long-lived keys in secret store
Poisoned PR executionNo untrusted checkout with secretsReviewing pull_request_target usage
Dependency abuseSHA/digest pinning + SCA gateLockfiles + pinned refs in diff
Untrusted artifactsSigning + provenance verificationDeploy fails on unsigned image
Secret sprawlPush protection + rotationHistorical secret scan

How Safeguard Helps

Safeguard runs as a native stage in your pipeline through a single CLI that performs SCA, SAST, and SBOM generation in one pass, then evaluates a policy gate you define as code. Because findings are prioritized by reachability and exploit signals, the gate blocks the handful of issues an attacker could actually use and lets the noise flow to backlog — so developers keep trusting the build. When a dependency needs bumping, auto-fix opens the pull request for you. If your pipeline lives in GitHub, our Safeguard vs GitHub comparison walks through where native Advanced Security stops and reachability-based gating begins.

Wire Safeguard into your pipeline free at app.safeguard.sh/register, or follow the CI integration guides at docs.safeguard.sh.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.