Back to Cheat Sheets

🔑 Inadequate Identity & Access Management

OWASP Top 10 CI/CD Security Risks - CICD-SEC-2

HIGH RISK

📋 What Is It?

Inadequate Identity and Access Management is the risk that arises when the many identities - human and machine - spread across the CI/CD ecosystem are too numerous, over-permissioned, stale, or poorly governed. It is not a single misconfigured account; it is the accumulated failure to consistently answer three questions for every identity: who or what is this, exactly what may it do, and should it still exist at all? SCM, CI orchestrators, registries, secrets managers, and cloud accounts each add their own users, tokens, and roles, producing identity sprawl with no single place that knows who holds what.

CICD-SEC-2 OWASP Rank
Identity Focus Area
Machine Bigger Risk

⚠️ Common Exploits

  • Over-Permissioned CI Tokens: A job that needs one repo holds org-wide or admin scope
  • Broad Non-Expiring PATs: A leaked personal token with every scope and no expiry is a master key
  • Local Bypass Accounts: Tool-local admins skip SSO, MFA, and central logging
  • Shared Bot Accounts: One "deploy-bot" used by many, unattributable, permission-bloated
  • Stale Access After Offboarding: SSO disabled but PATs, keys, and local accounts still valid
  • Standing External Access: Contractors and integrations with never-expiring write
  • Unrotated Deploy Keys: Robot tokens and SSH keys valid indefinitely
  • RBAC Sprawl: "Read-only" in one tool, admin in another

🔴 Attack Flow

1. Obtain an identity (leaked token, stale/shared account)
↓
2. Enumerate its reach (repos, CI, registry, cloud scopes)
↓
3. Abuse excess privilege (read secrets, edit pipeline)
↓
4. Pivot across systems: SCM → CI → registry → cloud
↓
5. IMPACT: Mint new tokens to persist - full supply-chain reach!

❌ Vulnerable Code

# .github/workflows/release.yml # A job that only needs to push ONE image, but the token can do everything. permissions: write-all # ← every scope, for every job jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Long-lived static cloud key with AdministratorAccess - run: | aws configure set aws_access_key_id ${{ secrets.AWS_KEY }} aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET }} ./push-image.sh # job only needs push to one ECR repo # Personal access token used as the pipeline credential: # scopes: repo, workflow, admin:org, delete_repo, write:packages # expiration: No expiration # stored in: a CI variable, a .env on a laptop, and a wiki page # Tool-local accounts outside the IdP: # jenkins -> local "admin" account, no MFA, not tied to SSO # registry -> basic-auth "team-svc" shared over chat

✅ Secure Code

# .github/workflows/release.yml # Least-privilege token + short-lived OIDC federation, no static cloud keys. permissions: contents: read # default: read-only jobs: publish: runs-on: ubuntu-latest permissions: contents: read id-token: write # mint a short-lived credential per run packages: write # exactly the scope this job needs steps: - uses: actions/checkout@v4 - uses: aws-actions/configure-aws-credentials@v4 with: # role's trust policy limits which repo/branch/env may assume it role-to-assume: arn:aws:iam::123456789012:role/push-one-ecr-repo aws-region: us-east-1 - run: ./push-image.sh # credential expires when the job ends # Governance settings (managed centrally, as code / via API): # - Every system fronted by the central IdP/SSO with MFA enforced # - ZERO local bypass accounts; no shared/generic logins # - PATs: short expiry, named owner, narrow scope, inventoried # - Deploy keys/robot tokens rotated on a schedule # - Offboarding revokes tokens + local accounts + cloud keys, not just SSO # - External collaborator + integration access time-boxed and reviewed

✓ Prevention Checklist

  • Maintain one inventory of every human & machine identity
  • Front every system with central IdP/SSO + enforced MFA
  • Eliminate local bypass and shared/generic accounts
  • Scope each token/service account to exactly its task
  • Give all PATs a short expiry and a named owner
  • Prefer short-lived OIDC federation over long-lived PATs
  • Rotate machine credentials and deploy keys on a schedule
  • Offboard across every plane, not just the IdP login
  • Time-box external collaborator & integration access
  • Run periodic access recertification; remove stale identities

🔍 Detection & Tools

Identity & Federation:

GitHub OIDC GitLab ID Tokens AWS IAM Roles HashiCorp Vault SPIFFE/SPIRE Central IdP + MFA

Audit & Discovery:

Legitify GitHub PAT/token audit AWS IAM Access Analyzer TruffleHog Access recertification

How to Test:

  • Enumerate token scopes (e.g. X-OAuth-Scopes header)
  • Search for local accounts that bypass SSO/MFA
  • List PATs and deploy keys with no expiry or owner
  • Verify offboarded users have zero valid credentials
  • Compare a user's RBAC across SCM, CI, and cloud for gaps
  • Confirm CI-to-cloud uses OIDC, not static access keys

🌍 Real-World (Incident Classes)

  • Leaked CI Token, Excessive Scope: A pipeline stored a long-lived token scoped to a whole SCM organisation instead of one repo; exposed via a build log or compromised runner and still valid (no expiry), it exposed private source across many projects - a pattern recurring in supply-chain post-mortems.
  • Stale Access After Offboarding: Deprovisioning disabled the IdP login but left the SCM PAT, CI local account, and cloud access key alive, giving a former insider a lasting path into code and infrastructure.
  • Shared Bot as Single Point of Failure: One generic automation account, reused by many humans and pipelines with a shared password and no MFA, accumulated the union of every task's permissions - its compromise yielded broad, unattributable access.
  • Self-Hosted Runner / Robot-Account Abuse: Over-privileged robot and runner identities reused across projects let a single foothold reach far beyond the workload that owned it.

📌 Quick Tips

  • DO NOT issue non-expiring org-wide PATs
  • DO NOT keep local accounts that bypass SSO/MFA
  • DO NOT share one bot account across teams
  • DO scope every identity to least privilege
  • DO use short-lived OIDC over static keys
  • DO offboard across every plane
  • DO recertify access on a schedule

📜 Compliance

Related Standards:

  • SOC 2 CC6.1-6.3 - Logical Access
  • ISO 27001 A.9 - Access Control
  • NIST 800-53 AC-2 / AC-6 - Least Privilege
  • PCI-DSS Req 7 & 8 - Access & Identity
  • CIS Control 5 & 6 - Account Management