frontdoor rule · FD001
GitHub Actions OIDC: no subject condition on IAM role
Nothing pins who may come through the door
Severity: critical. High instead when a real narrowing condition holds the
door (aws:PrincipalOrgID, sts:ExternalId), or when it is a SAML trust —
that admits every user of your identity provider, which is far too wide but
is not the open internet.
What it means
A federated trust has two halves: which issuer you accept tokens from, and which identity at that issuer you accept. This finding means the second half is missing.
Registering GitHub Actions as an OIDC provider does not scope anything to your repositories. GitHub mints a token for every repository on the platform. Without a condition on the subject claim, all of them satisfy the trust policy.
// Any GitHub repository in the world can assume this role.
"Condition": {
"StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }
}The audience condition above looks like a restriction. It is not one: it says the token was minted for AWS, not who minted it.
What an attacker does
Creates a free repository, adds a three-line workflow that requests an OIDC token, assumes your role. There is no exploit and no vulnerability — the policy is doing exactly what it says.
How to fix it
AWS
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:ACME/api:ref:refs/heads/main"
}
}Use StringEquals for an exact subject. StringLike is for a subject that
genuinely needs a wildcard, and every wildcard is a decision worth writing down.
GCP — two things must be true. The IAM binding has to name an identity
rather than the whole pool, and the provider should carry an
attributeCondition:
gcloud iam service-accounts add-iam-policy-binding SA_EMAIL \
--role=roles/iam.workloadIdentityUser \
--member='principalSet://iam.googleapis.com/projects/N/locations/global/workloadIdentityPools/POOL/attribute.repository/ACME/api'A member ending in /* binds every identity the pool will ever admit.
Azure — set a subject on the federated identity credential. A credential
with no subject and no claims-matching expression accepts any token the issuer
produces. A multi-tenant app registration (signInAudience of
AzureADMultipleOrgs) is the same problem by a different route: any tenant can
consent to it.
When this is a false positive
Rarely, but it happens: a GCP pool used by exactly one workload, where the
attributeCondition pins the subject and the binding is deliberately
pool-wide. frontdoor reads the condition and does not fire in that case. If
it fires anyway, that is a bug worth reporting.
frontdoor answers this once, when you run it; Secorvia checks it continuously across every account you connect, on a free tier that needs no card.