Who can walk into your AWS account from outside?
Your scanner checks what is wrong inside the account. Almost nothing checks who is allowed to walk in.
Every cloud scanner looks at what is wrong inside an account. Very few show you the doors leading in: the OIDC providers, SAML federations and cross-account roles that let someone outside the account assume a role in it. Those trusts are configuration, not vulnerabilities, so they rarely appear in a findings list at all.
Yet each one is a standing permission for an outsider to become an insider. A CI system, a monitoring vendor, a contractor's account, another cloud. If you cannot list them, you cannot say who has access.
The doors into a cloud account
In AWS, every door is the same object: a trust policy on an IAM role, saying which principal may call sts:AssumeRole or one of its variants. The principal is what changes.
- OIDC PROVIDERS
- GitHub Actions, GitLab CI, Terraform Cloud, CircleCI and others mint short-lived tokens. A role that trusts the provider exchanges those tokens for AWS credentials through
AssumeRoleWithWebIdentity. Conditions on the token's claims decide which pipeline gets in. - SAML FEDERATION
- Your identity provider (Okta, Entra ID, Google Workspace) signs an assertion, and a role trusts that provider. Every user the IdP will sign for is a potential entrant.
- CROSS-ACCOUNT ROLES
- A trust policy names another AWS account, or a role inside it. Common between your own accounts, and common with vendors and contractors.
- SAAS VENDOR ROLES
- A special case of cross-account trust. A security, monitoring or cost tool asks every customer to create a role trusting the vendor's account, protected (if you followed the setup guide) by an
sts:ExternalId.
Why scanners miss this
Nothing about a door looks broken. The trust policy is valid. The provider is registered correctly. The role works. A rule pack that asks "is there an OIDC provider?" gets the answer "yes" and moves on.
The question that matters is who the door admits, and that depends on conditions inside the policy. A role that trusts GitHub looks identical in an inventory whether it is pinned to one branch of one repository or open to every repository on GitHub. Only the Condition block tells them apart, and most tools do not parse it.
The second reason is reach. A door into a role with no permissions is harmless. A door into a role that can assume your deployment role is not. Seeing that means following the path after the door, which is the same graph problem described in what an attack path is.
The GitHub Actions wildcard problem
This is the best-known open door. Registering GitHub as an OIDC provider does not scope anything to your repositories. GitHub issues tokens to every repository on the platform. The only thing that narrows them is a condition on the sub claim.
Here is a trust condition that looks restrictive and is not:
"Condition": {
"StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }
}
The audience check proves the token was minted for AWS. It says nothing about which repository asked for it. Any GitHub user can create a repository, add a short workflow that requests a token, and assume this role. There is no exploit involved. The policy is doing exactly what it says.
The fix is one more line:
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:ACME/api:ref:refs/heads/main"
}
}
Two softer versions of the same mistake are worth checking too. A subject of repo:ACME/* trusts every repository in the organisation, including ones created next month. A subject of repo:ACME/api:* trusts every branch and tag, so anyone who can push a branch can use the role. The frontdoor docs cover each one: no subject condition (FD001), org-wide subjects (FD010) and any branch or tag (FD011).
What an unused identity provider costs you
An OIDC or SAML provider that no role references looks harmless. It grants nothing on its own. The risk is quieter than that.
Anyone who can edit a trust policy can point a role at an existing provider without creating anything new. Reviewers see a provider that has been there for two years and assume it is in use. Registering a new provider is a visible event. Attaching to an old one is not.
This is what the check looks like on an account with a GitLab provider left behind from a migration:
frontdoor 0.1.0 · acme-prod (111122223333) · 2026-10-03 09:12 UTC
| MEDIUM (1) ------------------------------------------------------------------
FD021 Unused identity provider: gitlab.com
arn:aws:iam::111122223333:oidc-provider/gitlab.com
Wrong This identity provider is registered but nothing references it.
Attacker Anyone who can write an IAM policy can point a role or service
account at this provider without creating anything that looks
new, and reviewers tend to assume a registered provider is in
use.
Evidence referenced by 0 roles or service accounts
Fix Delete the provider if the integration is gone.
- Confirm no role in any account in the organization uses it -
frontdoor only sees this account.
- Delete it with iam:DeleteOpenIDConnectProvider or
iam:DeleteSAMLProvider.
- Re-registering it later takes a minute; leaving it costs you
a blind spot.
Docs https://www.secorvia.com/docs/frontdoor/FD021
No external identity can enter your cloud.
1 finding: 1 medium.
The fix is a single CLI call, after you confirm that no account in the organisation still uses the provider. The full reasoning is on the FD021 rule page.
Trust types and what to check
| Trust type | What goes wrong | What to check | frontdoor rule |
|---|---|---|---|
| GitHub Actions OIDC | No sub condition, so any repository on GitHub can assume the role | An exact sub with StringEquals | FD001 |
| GitHub Actions OIDC | Subject pinned to the organisation only | One subject per repository and ref | FD010 |
| Cross-account vendor role | No sts:ExternalId, so another customer of the vendor can reach you | The ExternalId the vendor issued to you | FD013 |
| Cross-account role | Trusts an account nobody can identify | CloudTrail AssumeRole history for that account | FD014 |
| OIDC or SAML provider | Registered but referenced by no role | Whether any account in the organisation uses it | FD021 |
How to check your own account
We built frontdoor to answer this one question. It is a single binary, licensed under Apache 2.0, with no account and no signup. It uses the credentials you already have and makes only read calls.
go install github.com/secorvia/frontdoor/cmd/frontdoor@latest
frontdoor scan --aws
The AWS-managed SecurityAudit policy covers everything it reads. The report lists every external identity that can enter, what each one reaches, and the findings with a corrected Condition block built from your own issuer and repository names. Add --fail-on high to fail a CI job, or --format sarif to send findings to the GitHub Security tab.
It is safe to run against production. Every AWS call it makes is a List, Get or Describe. When a call is denied, the report says so, and the rules that depended on it lower their own severity rather than guess, so a partial scan never passes itself off as a clean one. That matters most in member accounts, where reading the Organization is often not allowed.
It also covers GCP workload identity federation and, in beta, Azure. When you scan AWS and GCP together it follows a path across the boundary, which neither cloud's own tooling shows. The frontdoor documentation lists all fifteen rules, and the cross-cloud rule explains how the two halves are joined.
What frontdoor does not do
It is deliberately narrow. It is not a CSPM, so it does not check bucket ACLs, security groups, encryption or compliance frameworks. It does not look for CVEs. It does not watch anything at runtime. It answers one question (who outside can get in, and how far do they get?) and stops. If you already run a broad scanner such as Prowler, run frontdoor alongside it rather than instead of it. If you are still choosing a full platform, our comparison of cloud security platforms sets out what each one covers.
Common misconceptions
"An audience condition restricts the role." It restricts which system the token was minted for, not who asked for it.
"Vendor roles are the vendor's problem." The trust policy is in your account. If it lacks an ExternalId, the confused deputy risk is yours.
"Old providers are inert." They are inert until someone attaches a role to them, and that change is easy to miss.
FAQ
How do I see which external identities can assume my AWS roles?
Read the trust policy of every IAM role and list each principal that is not a service. In practice that means OIDC providers, SAML providers and other AWS accounts. frontdoor does this in one command and also follows what each door can reach.
Is the aud condition enough for GitHub Actions OIDC?
No. The aud claim only shows the token was minted for AWS. Without a condition on sub, any repository on GitHub can assume the role.
Should I delete an unused OIDC provider?
Yes, once you have checked that no role in any account of the organisation references it. It grants nothing on its own, but it gives anyone who can edit a trust policy an easy door that reviewers will not notice.
What permissions does frontdoor need?
Read-only IAM, STS and Organizations calls. The AWS-managed SecurityAudit policy is a superset of what it uses, and it never writes anything.
Does frontdoor send data anywhere?
No. It has no telemetry and talks only to your cloud provider. The one optional network call, --resolve, opens a TLS handshake to an OIDC issuer to read its certificate chain and is off by default.
Is frontdoor a replacement for a CSPM?
No. It checks the trust into an account, not the configuration inside it. Use it alongside a CSPM or a broad scanner.
frontdoor is built by the team behind Secorvia, which runs the same analysis continuously across every connected account.