GitHub Actions OIDC Misconfig: When Any Repo Can Assume the Cloud Role
OIDC federation was supposed to kill long-lived CI keys. It does—when the trust policy's sub and aud conditions are tight. When they are not, any repository that can request a GitHub token for the right issuer may assume the cloud deploy role.
That is a supply-chain shaped cloud bug, and it shows up in public IAM snippets and Terraform modules constantly.
Read the trust conditions
AWS trust for GitHub usually checks:
token.actions.githubusercontent.com:audequals your STS audiencetoken.actions.githubusercontent.com:submatches a subject pattern
Weak patterns:
repo:org/*:*when only one repo should deploy- Missing environment claim while production relies on unprotected environments
- Allowing
pull_requestsubjects to assume a role meant forref:refs/heads/main - Correct repo name but
*for ref, letting feature branches ship credentials - Org rename leftovers that still trust the old slug alongside the new one
GCP workload identity pool attributes have the same failure mode with different JSON. Azure federated credentials misfire the same way when subject filters are empty.
Prove it from a repo you control
If the org allows researchers to use a fork or a sibling public repo in scope, add a workflow that requests id-token: write, assumes the role, and prints GetCallerIdentity. Do not deploy. Do not terraform apply. A successful assume from the wrong repo or branch is the finding.
No fork access? Argue from the trust policy alone if the program accepts configuration review, and cite exact claim mismatches. Some triage teams still want a live assume—ask rather than guessing.
Decode a sample OIDC JWT from a controlled workflow (header and claims only in the report) so you can show which sub you actually received versus what the trust policy allows. Compare job_workflow_ref claims too when the trust policy forgot them; reusable workflows change that field in ways teams do not expect.
If the org publishes Terraform modules that stamp repo:ORG/*:ref:refs/heads/* into every account, cite the module source. Template mistakes scale faster than one-off console clicks. A single over-broad trust policy shared by twenty accounts is one finding with enterprise impact—not twenty duplicates.
Adjacent footguns
pull_request_targetplus OIDC on the base repo can let a PR author run code in a context that still receives cloud roles- Reusable workflows from unverified publishers requesting the same audience
- Environments without required reviewers guarding
productionsubjects - Matrix jobs that expand into unexpected refs while still matching a loose
sub
Tie these back to whether the OIDC subject still matches. A beautiful OIDC setup next to a static AKIA in the same workflow is also worth filing—federation does not help if the old key remains.
Remediation language
Pin sub to repo:ORG/REPO:ref:refs/heads/main or to a specific environment subject. Separate plan and apply roles. Require environment protection rules. Prefer branch + environment claims together. Monitor CloudTrail for unexpected AssumeRoleWithWebIdentity callership.
OIDC removes static keys. It does not remove the need to name who is trusted. Write the subject like you mean it.