A CI/CD pipeline is one of the most privileged identities in any organisation. It reads source code, pulls dependencies, builds artefacts, holds signing keys and deploys to production, usually without a human watching each step. Pipelines are also full of non-human identities: repository tokens, cloud credentials, registry passwords, package publishing tokens and deployment keys. Compromising one pipeline component, or one token it holds, can give an attacker source code, secrets and the ability to ship malicious code to every customer. This guide covers the identities in CI/CD, how they are attacked, how to secure and monitor them, and how to measure progress. It is written for platform, DevOps, application security and identity teams.
Key takeaways
- Pipeline identities are privileged identities. Apply least privilege, short-lived credentials, owners and monitoring, as you would for administrators.
- Go keyless with tight trust policies. Replace long-lived cloud keys with OIDC workload identity federation, pinned to specific repositories, branches, workflows and environments; OWASP NHI6 warns that loose
subclaim conditions let unintended workflows assume roles. - Untrusted code and input must never run with privileged credentials. The SpotBugs to tj-actions chain began when a maintainer's personal access token leaked through a
pull_request_targetworkflow. - Pin and verify third-party actions, plugins and dependencies. Moving tags let one compromised token poison every consumer of an action.
- Publishing and signing credentials are the crown jewels. Use trusted publishing and keyless signing, but remember provenance proves which workflow built a package, not that the account driving it was legitimate.
The identities in a pipeline
Start with an inventory. Most pipelines hold more credentials than their owners expect, and each one is an identity that can be stolen or misused. The last column shows the preferred end state.
| Identity | Used for | Typical risk | Preferred replacement |
|---|---|---|---|
| Repository tokens (built-in job tokens, personal access tokens, app tokens) | Checkout, commenting, pushing, releasing | Default write permissions; personal tokens with broad scope tied to one maintainer | Job token with per-job least permissions; GitHub App installation tokens |
| Cloud credentials | Deploying infrastructure and applications | Static access keys stored as variables; admin roles | OIDC federation to a role per environment |
| Container registry credentials | Pushing and pulling images | Shared across projects; push access from untrusted builds | Federated, repository-scoped push rights from protected branches only |
| Package publishing tokens | Publishing to npm, PyPI and similar | Long-lived, account-wide tokens enabling supply chain attacks | Trusted publishing (OIDC) with token publishing disallowed |
| Signing keys | Signing code, artefacts and releases | Keys stored as files or variables | Keyless signing or keys in an HSM or KMS, never in the job |
| Deployment keys and SSH keys | Reaching servers and clusters | Unrotated, shared across environments | Short-lived certificates or pull-based deployment |
| Runner identities | Self-hosted runners' host credentials and cloud roles | Persistent runners reachable by untrusted jobs | Ephemeral, single-job runners with minimal host roles |
| Third-party integration tokens | Code scanning, notifications, test services | Over-scoped tokens held by vendors | Scoped app installations, reviewed with other OAuth grants |
Record an owner, purpose and expiry for each. See the Secrets Management Guide and the Cloud Workload Identity Guide.
How pipelines are attacked
The OWASP Top 10 CI/CD Security Risks gives a shared vocabulary for these paths. Four of its ten risks are directly about identity: CICD-SEC-2 Inadequate Identity and Access Management, CICD-SEC-4 Poisoned Pipeline Execution, CICD-SEC-5 Insufficient PBAC (Pipeline-Based Access Controls) and CICD-SEC-6 Insufficient Credential Hygiene.
| Attack path | OWASP CI/CD risk | Incident that illustrates it |
|---|---|---|
| Compromised third-party action or plugin | CICD-SEC-3, CICD-SEC-8 | tj-actions/changed-files and reviewdog (2025) |
| Untrusted pull request runs with secrets | CICD-SEC-4 | SpotBugs pull_request_target workflow (2024) |
| Self-propagating package malware stealing publishing tokens | CICD-SEC-3, CICD-SEC-6 | Shai-Hulud (2025), Miasma and Hades (2026) |
| Compromised CI platform or CI tool | CICD-SEC-8, CICD-SEC-9 | CircleCI (2023), Codecov (2021) |
| Editing pipeline configuration to reach production | CICD-SEC-1, CICD-SEC-5 | CI/CD pipeline exploitation demonstration (2024) |
| Over-trusting OIDC federation | CICD-SEC-2, CICD-SEC-7 | OWASP NHI6 example scenarios |
- Compromised third-party actions: CISA reported that the tj-actions/changed-files Compromise 2025 allowed disclosure of secrets including access keys, GitHub personal access tokens, npm tokens and private RSA keys, and that it was potentially enabled by the reviewdog/action-setup Compromise 2025. Unit 42 traced the reviewdog compromise to a maintainer's personal access token leaked from SpotBugs: see the SpotBugs Token Leak 2025.
- Self-propagating package malware: CISA said Shai-Hulud npm Worm 2025 compromised over 500 packages, targeted GitHub personal access tokens and cloud API keys, and spread by authenticating to npm as the compromised developer. The Miasma and Hades Worms 2026 repeated the pattern across npm, PyPI and Microsoft repositories.
- Compromised CI platforms: after the CircleCI Breach 2023, in which malware on an engineer's laptop stole a 2FA-backed SSO session, CircleCI recommended that all customers rotate their secrets, including OAuth tokens, project API tokens and SSH keys. In the Codecov Breach 2021, a modified Bash Uploader script could export credentials, tokens and keys passed through customers' CI runners.
- Poisoned pipeline execution: attackers submit pull requests or edit configuration so their code runs in a workflow with access to secrets. See the CI/CD Pipeline Exploitation 2024 case, where editing a pipeline made the deployment job add the researcher's SSH key to a production server.
- Over-trusting federation: OIDC trust policies that accept any repository or any branch let unintended workflows assume privileged roles (OWASP NHI6 Insecure Cloud Deployment Configurations).
Controls
Go keyless for cloud access
- Use the CI platform's OIDC tokens with cloud workload identity federation instead of stored access keys. GitHub describes the benefit plainly: no need to duplicate cloud credentials as long-lived secrets.
- Pin trust policies to the exact repository, workflow, branch or tag, and deployment environment. Review them like firewall rules (see the next section).
- Use separate roles per environment, with production roles only assumable from protected branches and environments.
See the NHI Authentication Guide and the Workload Identity Federation Guide.
Minimise token permissions
- Set the built-in job token to read-only by default and grant write permissions per job with the
permissionskey only where needed. - Replace personal access tokens with GitHub App installation tokens or fine-grained tokens scoped to specific repositories and expiring.
- Use trusted publishing to package registries where supported. npm says each trusted publish uses short-lived, cryptographically signed tokens specific to the workflow; it also accepts traditional tokens unless you select "Require two-factor authentication and disallow tokens", so turn that on.
Separate untrusted from privileged
- Run builds for pull requests from forks without secrets or write tokens.
- Avoid
pull_request_targetandworkflow_runwith untrusted code. GitHub says workflows using these triggers "must not explicitly check out untrusted code"; GitHub Security Lab recommends splitting work so the untrusted build runs unprivileged and a separate workflow handles write actions. - Require maintainer approval before running workflows from fork pull requests; GitHub lets you set approval requirements at repository, organisation or enterprise level.
- Use ephemeral runners for untrusted jobs. GitHub warns that self-hosted runners "can be persistently compromised by untrusted code" and should almost never be used for public repositories; just-in-time runners perform at most one job before removal.
Control dependencies and actions
- Pin third-party actions and plugins to full-length commit SHAs, not tags, and verify the SHA comes from the action's repository rather than a fork. GitHub now also offers workflow dependency lockfiles, which it describes as the strongest protection available.
- Maintain an allowlist of approved actions and protect
.github/workflowswith CODEOWNERS review. - Route dependency installs through a proxy registry with policy, use lock files, and track components in an SBOM. See the SBOM Guide.
Protect secrets that remain
- Store them in a secrets manager or environment-scoped secrets, available only to specific jobs and protected branches; use required reviewers on production environments so a job cannot read environment secrets until approved.
- Remember that any user with write access to a GitHub repository can read all secrets configured in it, so scope write access as tightly as the secrets.
- Do not use structured data (JSON, YAML) as a secret, because log redaction relies on exact matches; register derived values as secrets.
- Rotate after any suspected exposure, and immediately after a CI platform or dependency compromise. See the Secrets Rotation Runbook Template.
Sign and verify
- Sign artefacts and generate provenance. With Sigstore keyless signing, Fulcio issues short-lived certificates binding an ephemeral key to an OIDC identity and Rekor logs the signing event. See the Code Signing Security Guide.
- Verify signatures and provenance, including the expected signer identity, before deployment.
- Use SLSA build levels as targets: Build L2 requires a hosted platform that signs provenance; Build L3 requires a hardened platform that keeps provenance signing material away from user-defined build steps.
Designing federation trust policies
Federation removes stored keys but moves the risk into the trust policy. The cloud provider checks the OIDC token's subject and other claims against the conditions you write; if those conditions are loose, any workflow that can mint a token may assume the role. AWS, for example, requires the GitHub organisation when you create a role for GitHub Actions in the console, but the repository and branch default to a wildcard if left blank.
| Condition | Example value (GitHub Actions) | What it prevents |
|---|---|---|
| Audience | sts.amazonaws.com | Tokens minted for another relying party being replayed |
| Subject: repository and environment | repo:octo-org/octo-repo:environment:prod | Other repositories, forks and non-production jobs assuming the production role |
| Subject: branch or tag | repo:my-org/my-repo:ref:refs/heads/main | Feature branches and pull request jobs reaching deployment roles |
| Repository properties | repo_property_* claims | Attribute-based access that survives repository sprawl without wildcards |
| Immutable identifiers | Owner and repository IDs in the default subject format for repositories created after 15 July 2026 | A deleted and re-created repository with the same name inheriting trust |
Treat every wildcard in a subject condition as a finding. Review trust policies whenever repositories are renamed, transferred or archived. For AWS specifics, see the AWS IAM Policies and Roles Guide.
AI agents in pipelines
AI agents increasingly run inside CI/CD to review code, fix issues, triage and respond to comments. They process untrusted text such as issue bodies, pull request descriptions and commit messages, which makes them a prompt injection target with whatever credentials the job holds.
- No privileged tokens on outsider events: an agent triggered by an external issue or pull request must not run with write tokens, cloud roles or secrets. Treat its input like untrusted code in a
pull_request_targetworkflow. - Least privilege per task: give a review agent read and comment permissions only; route any write action through a separate, approved workflow.
- Guard agent configuration: in the Miasma wave, attackers pushed trigger files into
.claude,.gemini,.cursorand.vscodefolders so the payload ran when a repository was opened. Put these paths under CODEOWNERS review and scan for them. - No self-approval: GitHub lets you prevent Actions workflows from creating or approving pull requests; keep that off for agents.
See the AI Coding Agents Security Guide.
Detection and monitoring
NIST SP 800-204D calls for real-time monitoring and alerting on CI/CD servers, especially activity that might indicate exfiltration of sensitive data or tampering with builds. In practice, watch both the pipeline platform and what pipeline identities do downstream.
- Configuration changes: alert on edits to workflow files, branch protection, deployment environments, runner groups and federation trust policies. GitHub's audit log records events such as
org.update_actions_secret. - Tag and release tampering: alert when tags on actions you consume move to a new commit, the technique used against tj-actions and reviewdog.
- Pipeline role behaviour: monitor cloud activity by pipeline roles for new actions, new regions or sources outside your runner ranges.
- Runner egress: baseline outbound connections from runners and alert on unknown destinations; CISA advised blocking webhook.site during Shai-Hulud.
- Secrets in output: scan logs, artefacts and caches for secrets, and delete and rotate when one appears unredacted.
- Publishing anomalies: alert on package versions published outside the release workflow or from unexpected identities.
See the GitHub and GitHub Actions Security Guide and, when a pipeline credential leaks, the Cloud Credential Compromise Playbook.
Framework mapping
Control IDs were checked against NIST CSF 2.0, the NIST SP 800-53 Rev. 5 catalogue, NIST SP 800-218, the CIS Controls safeguard text and the OWASP lists. ISO/IEC 27001:2022 Annex A control names come from a published Annex A index. This is an indicative NHI Mgmt Group alignment, not an official crosswalk.
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev. 5 | NIST SP 800-218 (SSDF) | ISO/IEC 27001:2022 Annex A | CIS Controls v8.1 | OWASP |
|---|---|---|---|---|---|---|
| Pipeline credential inventory and lifecycle | PR.AA-01 | IA-5 | PO.5.1 | 5.17 Authentication information | 5.5 | NHI7, CICD-SEC-6 |
| Least privilege for pipeline identities and federation | PR.AA-05 | AC-6 | PO.5.1 | 8.2 Privileged access rights | 6.8 | NHI5, NHI6, CICD-SEC-2, CICD-SEC-5 |
| Separation of untrusted, test and production | PR.IR-01 | CM-5 | PO.5.1 | 8.31 Separation of development, test and production environments | 16.8 | NHI8, CICD-SEC-4 |
| Protecting code and pipeline configuration | PR.PS-01 | CM-3, CM-5, SA-10 | PS.1.1 | 8.4 Access to source code, 8.32 Change management | 16.1 | CICD-SEC-1 |
| Third-party actions and dependencies | GV.SC-05 | SR-3 | PW.4.1 | 8.25 Secure development life cycle | 16.4, 16.5 | NHI3, CICD-SEC-3, CICD-SEC-8 |
| Signing, provenance and verification | PR.PS-06 | SI-7(15), SR-4, SC-12 | PS.2.1, PS.3.2 | 8.24 Use of cryptography | 16.1 | CICD-SEC-9 |
| Logging and monitoring of pipelines | DE.CM-09 | AU-6, SI-4 | PO.5.1 | 8.15 Logging, 8.16 Monitoring activities | 8.2 | CICD-SEC-10 |
Common failure patterns
- A personal token in a privileged workflow: Unit 42 reports that a SpotBugs maintainer modified a workflow to use their own personal access token, and days later an attacker's pull request exploited a
pull_request_targetworkflow to leak it. See the SpotBugs Token Leak 2025. - Trusting mutable tags: with a stolen token, the attacker repointed existing tj-actions/changed-files tags to a malicious commit, so every workflow using a tag ran it.
- Over-broad tokens shared across projects: the leaked reviewdog maintainer token was stored as a secret in one project yet had access to another organisation's repositories.
- Wildcard federation: a role trusts the identity provider and organisation but not a specific repository, branch or environment, so any workflow can deploy.
- Incomplete rotation after an incident: the Miasma write-up notes the durabletask contributor account appears to have been used twice, a month apart; rotation must cover every token, session and account a compromised host could reach.
- Provenance treated as proof of intent: in Miasma, packages were published through GitHub Actions OIDC with valid provenance from a compromised employee account.
- Repository write access treated as low risk: pipelines turn write access into production access, as the CI/CD Pipeline Exploitation 2024 case shows.
For the wider pattern, see Top 10 Supply Chain Attacks.
Metrics that show progress
| Metric | What it shows | Target direction |
|---|---|---|
| Pipelines deploying with stored cloud keys versus OIDC | Progress to keyless access | Stored keys down to zero |
| Federation trust policies with wildcard subject conditions | Over-trusting federation | Down to zero |
| Third-party action references pinned to full SHAs or a lockfile | Exposure to tag tampering | Up, towards 100% |
| Workflows with default write job tokens | Token over-permission | Down |
| Personal access tokens used in CI | Credentials tied to individuals | Down |
| Packages published by trusted publishing with token publishing disallowed | Publishing token risk | Up |
| Releases with signed, verified provenance at deploy | Artefact integrity | Up |
| Time to rotate all pipeline secrets after a supplier incident | Response readiness | Down |
Implementation roadmap
| Phase | Focus | Actions |
|---|---|---|
| First 30 days | Visibility and quick wins | Inventory pipeline credentials and owners; set job tokens to read-only by default; find pull_request_target workflows that check out untrusted code; enable approval for fork pull requests; turn on secret scanning |
| Days 31 to 90 | Remove standing secrets | Move cloud deployments to OIDC with pinned trust policies; replace personal access tokens with app tokens; pin actions to SHAs and allowlist them; protect workflow files with CODEOWNERS |
| Days 91 to 180 | Integrity and isolation | Adopt trusted publishing and disallow tokens; sign artefacts and verify provenance at deploy; move untrusted jobs to ephemeral runners; add detections for tag moves and trust policy changes |
| Beyond 180 days | Sustain and mature | Target SLSA Build L3 for critical releases; rehearse full secret rotation after a supplier compromise; review agent permissions in pipelines each quarter |
For the broader programme, see the CI/CD Security Guide and the Software Supply Chain Security Guide.
Practitioner checklist
- Inventory every credential used by pipelines, runners and CI integrations, with an owner and expiry.
- Replace stored cloud keys with OIDC federation pinned to repository, branch, workflow and environment.
- Remove wildcard subject conditions from federation trust policies.
- Default job tokens to read-only; replace personal access tokens with scoped, expiring app tokens.
- Use trusted publishing for package registries and disallow token-based publishing.
- Keep secrets away from untrusted builds; never check out untrusted code in
pull_request_targetworkflows. - Use ephemeral runners for untrusted jobs and require approval for fork pull requests.
- Pin actions and plugins to full commit SHAs or a lockfile, and allowlist them.
- Sign artefacts and verify signatures and provenance before deploy.
- Give AI agents in pipelines read-only tokens on outsider events and review their configuration files.
- Monitor workflow, tag and trust policy changes and pipeline role activity; rehearse full rotation.
Standards and references
- OWASP: Top 10 CI/CD Security Risks
- OWASP: NHI6:2025 Insecure Cloud Deployment Configurations
- NIST: SP 800-218, Secure Software Development Framework (SSDF) Version 1.1
- NIST: SP 800-204D, Strategies for the Integration of Software Supply Chain Security in DevSecOps CI/CD Pipelines
- SLSA: Security levels
- GitHub Docs: Secure use reference for GitHub Actions
- GitHub Docs: OpenID Connect
- GitHub Security Lab: Keeping your GitHub Actions and workflows secure, Preventing pwn requests
- npm Docs: Trusted publishing for npm packages
- Sigstore: Keyless signing overview
- CISA: Supply Chain Compromise of Third-Party tj-actions/changed-files and reviewdog/action-setup
- Palo Alto Networks Unit 42: GitHub Actions Supply Chain Attack
Related NHI Mgmt Group resources: Secrets Management Guide · Cloud Workload Identity Guide · GitHub and GitHub Actions Security Guide · Software Supply Chain Security Guide · AI Coding Agents Security Guide