CI/CD pipelines hold some of the most powerful machine credentials in any organisation, and they are often wired to the cloud with static keys or loosely trusted OIDC roles that anyone can abuse. OWASP ranks this as NHI6:2025 Insecure Cloud Deployment Configurations in the Non-Human Identities Top 10. Despite the broad name, OWASP's scope is specific: how deployment pipelines authenticate to cloud services, through static service account credentials or OpenID Connect (OIDC) federation, and how each goes wrong. This guide turns the OWASP entry into concrete trust policy, workflow and assessment requirements for GitHub Actions, GitLab CI, AWS, Azure and Google Cloud. It is for platform engineers, DevSecOps teams and cloud security reviewers.
Key takeaways
- OIDC is necessary but not sufficient. OWASP recommends replacing static pipeline keys with OIDC, but a trust policy that checks only the issuer and audience lets any user of a shared platform such as github.com or gitlab.com assume the role.
- The subject claim is the control. Pin
sub(or an equivalent attribute condition) to a specific organisation, repository and branch or environment, using numeric IDs where the platform provides them. - Static keys must leave the pipeline. Hard-coded or stored cloud keys leak through repositories, logs and artifacts, bypass MFA and give persistent access.
- The pipeline's role is a production identity. Scope it per repository and environment, keep sessions short, and gate production behind protected environments with reviewers.
- Easy to detect, so check it. OWASP rates detectability as easy: trust policies and workflow files can be scanned automatically, which makes this one of the quickest NHI risks to measure.
What it is and who it applies to
OWASP explains that CI/CD pipelines usually authenticate to cloud services in one of two ways: a service account with static credentials, or OIDC federation that exchanges a platform-issued token for short-lived cloud credentials. Static credentials are insecure because they can leak through repositories, logs or configuration files and provide persistent access that bypasses MFA. OIDC is safer because tokens are short-lived, but it can be undermined by weak token validation or missing restrictions on claims such as the subject.
| OWASP rating factor | NHI6 rating | Why |
|---|---|---|
| Exploitability | Average | Hard to find from outside, but someone with basic read access inside the environment can often find a weak trust policy or stored key. |
| Prevalence | Common | Pipelines are set up quickly by application teams, often from copied examples. |
| Detectability | Easy | Trust policies and workflow files are machine readable. |
| Technical impact | Severe | Pipelines usually hold high privilege, so compromise enables supply chain attacks or production access. |
| Business impact | Specific | Depends on what the deployment identity can change. |
OWASP's two example scenarios define the scope: an AWS IAM role that trusts a public OIDC provider (GitHub or GitLab) without restricting the subject claim, so any user of that platform may assume it; and Azure service principal credentials hard-coded in a GitHub Actions configuration, readable by anyone with access to the file. OWASP also cites the Cloud Security Alliance NHI report, which attributes 32% of NHI-related incidents to configuration errors. It applies to anyone running GitHub Actions, GitLab CI, Azure Pipelines, Jenkins, CircleCI, Terraform runners or similar tools that deploy to cloud. For pipeline security beyond identity, see the CI/CD Security Guide and the Top 10 CI/CD Security Risks.
Structure and key requirements
OWASP's prevention guidance has three parts: use OIDC and validate issuer, audience and claims; enforce least privilege on pipeline permissions and trust relationships; and avoid hard-coded credentials by using a secrets manager and scanning repositories and configuration. The table compares the two authentication patterns OWASP describes.
| Aspect | Static credentials (access keys, client secrets, SA keys) | OIDC federation |
|---|---|---|
| Lifetime | Until rotated or revoked, often years | Platform token minutes; cloud session typically one hour (AWS configure-aws-credentials default 3,600 seconds, range 15 minutes to 12 hours) |
| Where it can leak | Repository files, CI variables, logs, artifacts, runner disks, forks | Only the short-lived token, during the job |
| Main misconfiguration | Stored at all; broad permissions; no rotation | Trust policy without a subject or attribute condition; wildcard subject; over-broad role |
| Revocation after compromise | Must find and rotate every copy | Tighten trust policy; tokens expire |
| When still justified | Platform cannot issue OIDC tokens (some self-hosted CI) and no vault broker is available | Default for every platform that supports it |
For the federation mechanics across clouds, see the Workload Identity Federation Guide.
Identity and NHI requirements
Trust policy requirements by platform
| Cloud | Must check | Specifics |
|---|---|---|
AWS (IAM role, AssumeRoleWithWebIdentity) | aud and sub | For GitHub, token.actions.githubusercontent.com:aud equals sts.amazonaws.com and token.actions.githubusercontent.com:sub equals the exact repository and ref or environment. The configure-aws-credentials README states that without a condition any GitHub user or repository could potentially assume the role, and warns against ForAllValues: in Allow statements because it can evaluate true when a claim is missing. For GitLab, condition on gitlab.com:sub such as project_path:mygroup/myproject:ref_type:branch:ref:main. |
| Azure (federated identity credential on an app registration or user-assigned managed identity) | issuer, subject, audience | Microsoft documents that all three must match the external token case-sensitively; GitHub examples use issuer https://token.actions.githubusercontent.com, subject repo:org/repo:ref:refs/heads/main and audience api://AzureADTokenExchange. One credential per repository and environment. |
| Google Cloud (workload identity pool provider) | attribute condition | Google states that providers with shared issuers (GitHub, GitLab SaaS, HCP Terraform) must use an attribute condition restricting access to your own organisation, for example assertion.repository_owner=='ORG', optionally with a branch. Prefer numeric ID attributes, avoid granting to the whole pool principalSet, use one service account per workload, and keep the pool in a dedicated project. |
GitHub Actions subject claim choices
- Environment:
repo:ORG/REPO:environment:NAMEis issued whenever the job references an environment, which lets you combine OIDC with environment protection rules. This is the strongest default for production deployments. - Branch or tag:
repo:ORG/REPO:ref:refs/heads/BRANCHis used when there is no environment and the run is not a pull request. - Pull request:
repo:ORG/REPO:pull_request. Never grant a deployment role to this subject. - Immutable subjects: GitHub documents that repositories created after 15 July 2026 use a subject that embeds owner and repository IDs (
repo:OWNER@OWNER-ID/REPO@REPO-ID:...), which defeats name reuse after renames; older repositories can opt in. Check which format your trust policies expect before migrating. - Custom templates: organisation admins can change the subject with
include_claim_keys, for example addingjob_workflow_refto pin a reusable deployment workflow. Create the matching cloud condition first or tokens will be rejected.
Pipeline-side controls
- Token permissions: set the default
GITHUB_TOKENto read-only and grantid-token: writeonly to the deployment job. GitHub notes thatid-token: writeonly allows requesting the OIDC token. - Untrusted triggers: avoid
pull_request_targetfor jobs with secrets or cloud roles and never check out untrusted code in it. The Ultralytics PyPI compromise combined apull_request_targetinjection with a long-lived PyPI token that stayed valid after the move to short-lived publishing. - Credential persistence: set
persist-credentials: falseonactions/checkoutand never upload the checkout directory as an artifact; the ArtiPACKED research found tokens in public artifacts this way. - Runners: GitHub advises self-hosted runners should almost never be used for public repositories; use ephemeral runners for deployment jobs.
- Third-party actions: pin to full commit SHAs, since tags can be moved. See the GitHub and GitHub Actions Security Guide.
Choosing a federation pattern
| Pattern | Use when | Trade-off |
|---|---|---|
Direct federated access (Google Cloud principal:// or principalSet:// grants on resources) | The target APIs support federated principals; Google documents this as the default recommendation | Some services do not support it; check the supported services list first |
Federation plus service account impersonation (Google Cloud roles/iam.workloadIdentityUser) or role assumption (AWS) | The API needs a service account, or you want one auditable identity per workload | An extra identity to own and scope; avoid granting impersonation to the whole pool |
| Vault or secrets broker issuing dynamic cloud credentials | The CI platform cannot issue OIDC tokens, or many clouds and databases must be reached from one trust point | The broker becomes a high-value target and its own authentication must be federated, not a static token |
| Static key in a secrets manager (last resort) | Legacy tooling with no federation path | Needs rotation, a dated exception and detection on use from unexpected locations |
For brokered secrets, see the Secrets Management Guide. Whatever the pattern, keep the deployment identity separate from the runtime identity of the application it deploys, so a compromised build cannot inherit production data access and a compromised application cannot redeploy itself.
Implementation steps
- Inventory deployment identities. List every cloud credential a pipeline uses: CI variables and secrets containing keys, OIDC providers registered in each cloud account, federated credentials and workload identity providers, and the roles they map to.
- Scan trust policies. Flag any role trusting
token.actions.githubusercontent.com,gitlab.comor another shared issuer without a subject condition, with wildcards in the organisation or repository part, or usingForAllValues:. Fix these first: hackingthe.cloud documents that the AWS console can create a GitLab trust without a subject condition, and an attacker needs only a gitlab.com account and the role ARN. - Scan for static keys. Search repositories, CI variables, container images and build logs for cloud keys, and turn on secret scanning with push protection. See the OWASP NHI Top 10 NHI2 Secret Leakage Guide.
- Migrate one pipeline as a pilot. Create a role per repository and environment with an exact subject condition, add
id-token: writeto the deploy job, and run it alongside the old key for one release. - Revoke the old credential. Deactivate the static key first (AWS allows deactivation before deletion), watch for failures for a release cycle, then delete. Keeping the old key is how the Ultralytics attacker kept publishing.
- Right-size the role. Remove admin policies from deployment roles; scope them to the stacks, buckets or registries the pipeline deploys. See the OWASP NHI Top 10 NHI5 Overprivileged NHI Guide.
- Add preventive guardrails. Block creation of long-lived keys for pipeline identities through organisation policy (for example the Google Cloud constraint that disables service account key creation, or an SCP denying
iam:CreateAccessKey), and require trust policy changes to go through reviewed infrastructure as code. - Roll back if needed. If OIDC fails in production, re-enable the deactivated key for that pipeline only, with a fixed expiry date, and fix the condition rather than widening it.
Small versus large organisations: a small team can migrate by hand in a day per cloud and use the platform's built-in secret scanning. A large estate needs a central role vending pattern (one module that creates OIDC roles with a mandatory subject condition), a policy check that rejects trust policies without it, and a dashboard of remaining static keys by team. See the CI/CD Pipeline Identity Security Guide.
Evidence and assessment
What to log and monitor: CloudTrail AssumeRoleWithWebIdentity events (check the subject and source repository against the expected list), Entra sign-in logs for service principals using federated credentials, Google Cloud STS and service account impersonation audit logs, changes to OIDC providers and trust policies, and any use of a pipeline identity from outside the CI platform's network or runner fleet. Relevant ATT&CK techniques include T1552.001 Credentials In Files, T1078.004 Cloud Accounts and T1195.002 Compromise Software Supply Chain.
Metrics:
- Percentage of pipelines deploying to cloud through OIDC rather than static credentials.
- Number of OIDC trust policies without a subject or attribute condition (target: zero).
- Number of static cloud keys stored in CI variables or secrets, and their age.
- Percentage of production deployment jobs gated by a protected environment with required reviewers.
- Time from leaked pipeline secret detection to revocation.
- Percentage of workflows with read-only default token permissions.
Assessment evidence: exported trust policies with their conditions, list of federated credentials per identity, secret scanning reports, organisation policy showing key creation blocked, and workflow permission settings.
Mapping to other frameworks
| Framework | Reference | Relevance to NHI6 |
|---|---|---|
| NIST SP 800-53 Rev. 5 | IA-5(7) No Embedded Unencrypted Static Authenticators; IA-5 Authenticator Management | No keys in code or pipeline configuration |
| NIST SP 800-53 Rev. 5 | AC-6 Least Privilege; CM-6 Configuration Settings; SA-10 Developer Configuration Management | Scoped deployment roles, hardened pipeline settings, controlled changes |
| NIST CSF 2.0 | PR.AA-01; PR.AA-05; PR.PS-01; PR.PS-06 | Service identities, least privilege, configuration management, secure development |
| CIS Controls v8.1 | 4.1 Establish and Maintain a Secure Configuration Process; 16.1 Establish and Maintain a Secure Application Development Process | Pipeline configuration baselines and secure delivery |
| ISO/IEC 27001:2022 Annex A | 5.17 Authentication information; 8.9 Configuration management; 8.25 Secure development life cycle | Credential handling, configuration and SDLC controls |
| MITRE ATT&CK v20 | T1552.001; T1078.004; T1195.002; T1199 Trusted Relationship | Key theft from files, cloud account use, supply chain abuse |
| OWASP NHI Top 10 2025 | NHI2, NHI5, NHI7, NHI8 | Leaked, overprivileged and long-lived pipeline credentials; shared credentials across environments |
Related risks: long-lived pipeline keys are covered in the OWASP NHI Top 10 NHI7 Long-Lived Secrets Guide, and a single deployment role used for test and production in the OWASP NHI Top 10 NHI8 Environment Isolation Guide.
Common pitfalls
- Audience-only trust. The audience proves the token came from the platform, not from your repository. Every shared issuer needs a subject or attribute condition.
- Wildcard subjects.
repo:my-org/*lets any repository in the organisation, including a new one created by a compromised member, deploy to production. Pin repository and environment. - Migrating without revoking. Moving to OIDC while the old key still works leaves the original attack path open, as Ultralytics showed.
- Disabling guardrails for convenience. In the CISA Private-CISA GitHub leak, a contractor had disabled secret push protection, and administrative AWS GovCloud keys sat in a public repository for about six months.
- Trusting log masking. GitHub states redaction is not guaranteed, notably for transformed or structured secrets; rotate any secret that appears in a log.
- One admin role for every pipeline. A single high-privilege deployment role turns any compromised repository into full account access.
Practitioner checklist
- Inventory every pipeline-to-cloud credential and OIDC trust.
- Add exact subject or attribute conditions to every trust on a shared issuer; remove wildcards and
ForAllValues:. - Use one role or federated credential per repository and environment, bound to protected environments for production.
- Replace static cloud keys with OIDC, then deactivate and delete the old keys.
- Block long-lived key creation for pipeline identities with organisation policy.
- Set default workflow token permissions to read-only and grant
id-token: writeper job. - Avoid
pull_request_targetfor privileged jobs and setpersist-credentials: false. - Enable secret scanning with push protection and forbid disabling it without approval.
- Alert on role assumptions from unexpected repositories or subjects.
Standards and references
- OWASP: NHI6:2025 Insecure Cloud Deployment Configurations
- NIST: SP 800-53 Rev. 5
- GitHub: OpenID Connect reference
- GitHub: Secure use reference
- AWS: configure-aws-credentials action
- Microsoft: Workload identity federation
- Google Cloud: Workload identity federation with deployment pipelines
- Hacking the Cloud: Exploiting misconfigured GitLab OIDC AWS IAM roles
Related NHI Mgmt Group resources: CI/CD Pipeline Identity Security Guide · Workload Identity Federation Guide · Secrets Management Guide · CI/CD Pipeline Compromise Playbook