Moving workloads from static keys to federated and managed identities removes the leaked-key problem, but a loosely written trust rule can hand the same access to a workload you do not control. Workload identity federation, managed identities, IAM roles for compute and Kubernetes service account tokens all replace secrets with short-lived tokens issued on the strength of a trust decision. When that decision checks the wrong claim, accepts a wildcard, or trusts a whole pool, the attacker no longer needs to steal a key: they only need to run code somewhere the trust rule accepts. This page ranks the ten workload identity mistakes we see most often across AWS, Microsoft Entra ID, Google Cloud and Kubernetes, with why each matters, how to detect it and the first fix. It is for cloud platform, IAM and DevSecOps teams who already use, or are migrating to, keyless workload identity. The ranking is NHI Mgmt Group's editorial view; for the full control set see the Cloud Workload Identity Guide.
The Top 10 at a glance
| # | Mistake | Why attackers like it | First fix |
|---|---|---|---|
| 1 | Trust rules that check too little (wildcard or missing subject) | Any repository, branch or tenant that can mint a token can assume the role | Pin subject to organisation, repository and branch or environment; check audience |
| 2 | Mapping identity to mutable or reusable claims | Renamed or recreated repositories, projects and accounts inherit access | Map to immutable IDs such as repository_id and owner IDs |
| 3 | Static keys kept "just in case" after federation | The old key still works and is the easiest thing to steal | Delete keys, then block their creation by policy |
| 4 | Instance metadata reachable without protection | SSRF turns a web bug into cloud role credentials | Require IMDSv2; block metadata from pods that do not need it |
| 5 | Node or default identities doing workload jobs | Every pod or app on the host shares one broad identity | One identity per workload; least-privilege node identity |
| 6 | One identity shared by many workloads or environments | Compromise of the weakest workload grants the strongest workload's rights | Separate identity per application per environment |
| 7 | Broad trust boundaries (whole pools, projects or clusters) | An untrusted cluster or team inherits a trusted identity | Separate trust domains; grant to specific principals |
| 8 | Over-broad permissions on the federated role | Short-lived tokens with admin rights are still admin rights | Scope to named resources; review with access analysis |
| 9 | Uncontrolled change rights over trust configuration | Whoever edits the trust rule can add their own workload | Restrict who can create or edit federation, alert on change |
| 10 | No logging that ties tokens back to the workload | Misuse is attributed to "the role" and never traced | Enable token exchange logs and keep the federated subject |
The order reflects how often NHI Mgmt Group sees each mistake in reviews and in breach analysis; the first fix column is our recommendation.
1. Trust rules that check too little
The mistake: a role trusts the GitHub, GitLab or Terraform Cloud OIDC issuer but the condition on the subject is a wildcard, covers the whole organisation, or is missing, and the audience is not checked.
Why it matters: these issuers are multi-tenant. Anyone can create a repository and request a token from the same issuer. AWS states in its IAM documentation that if you do not limit token.actions.githubusercontent.com:sub to a specific organisation or repository, GitHub Actions from organisations or repositories outside your control can assume the role. AWS now rejects a GitHub trust policy where the sub condition is missing, null or only a wildcard, but repo:YourOrg/* still passes, and that lets any repository in the organisation, including a fork-driven or newly created one, assume a production role. GitHub's own reference says you must define at least one condition so that untrusted repositories cannot request access tokens.
Detect it: list every role or federated credential that trusts an external issuer and flag conditions using StringLike with *, conditions that omit aud, and Google Cloud providers with no attribute condition. In AWS, IAM Access Analyzer external access findings help surface roles assumable from outside the account.
The fix: use StringEquals on aud (sts.amazonaws.com for AWS) and pin sub to the repository plus a protected branch or a GitHub environment with protection rules, which AWS strongly recommends. In Google Cloud, add an attribute condition that limits tokens to your organisation and require the audience to be the provider's full URL. In Microsoft Entra ID, a federated identity credential must match issuer and subject exactly and wildcards are not supported, so the risk there is a subject that is broader than intended (for example a branch any contributor can push to) rather than a pattern. See the GitHub Actions OIDC Federation Hardening Guide.
2. Mapping identity to mutable or reusable claims
The mistake: the trust rule or attribute mapping keys on names: repository name, organisation name, email address, project slug.
Why it matters: names can be renamed, deleted and recreated by someone else. Google's workload identity federation best practices tell you to map only to immutable attributes, to prefer unique IDs over reusable values such as email addresses, and to rely only on attributes the identity provider guarantees are stable. GitHub exposes repository_id and repository_owner_id claims for this reason, lets administrators customise the subject template with them through the REST API, and states that repositories created after 15 July 2026 use an immutable default subject format that includes owner and repository IDs (not available on GitHub Enterprise Server, as documented in November 2026).
Detect it: review attribute mappings and subject templates for name-only claims; check whether older repositories still use the name-based repo:org/name subject.
The fix: include immutable ID claims in the subject template and in the trust condition, and update the cloud-side condition before changing the template, because GitHub warns that a token with a new subject format may otherwise be rejected.
3. Static keys kept "just in case" after federation
The mistake: the pipeline now uses OIDC, but the old access key, service account key or client secret is still valid and still stored in CI variables, a vault path or a laptop.
Why it matters: federation only reduces risk if the long-lived alternative is gone. Leaked static cloud keys remain a common path into cloud estates, as in the Codecov breach, where a leaked cloud storage key let attackers alter a CI script, and the Codefinger S3 ransomware campaign, which used stolen AWS keys.
Detect it: after migration, list keys and secrets on the same identity with last-used dates: AWS IAM credential report and access key last used, Google Cloud service account key usage insights, Entra app registration certificates and secrets with expiry and sign-in logs.
The fix: disable, wait one release cycle, then delete. Prevent new keys with policy, for example the Google Cloud organisation policy constraint that disables service account key creation and an SCP that denies iam:CreateAccessKey outside a break-glass role. See the GKE keys to Workload Identity Federation migration.
4. Instance metadata reachable without protection
The mistake: EC2 instances still allow IMDSv1, or containers can reach the node's metadata endpoint at 169.254.169.254.
Why it matters: the metadata service hands out the instance role's credentials to any process that can make an HTTP request to it, which is ATT&CK T1552.005 Cloud Instance Metadata API. In the Capital One breach, a server-side request forgery against a misconfigured firewall retrieved role credentials this way. IMDSv2 requires a session token obtained with a PUT request, rejects PUT requests carrying X-Forwarded-For, and defaults the response hop limit to 1, which together defeat most SSRF and proxy paths.
Detect it: AWS publishes three CloudWatch metrics for this: MetadataNoToken (IMDSv1 calls still being made), MetadataNoTokenRejected (calls rejected after IMDSv1 is disabled) and ImdsV1Enabled (instances where httpTokens is still optional).
The fix: drive MetadataNoToken to zero, set HttpTokens=required, set the account default with modify-instance-metadata-defaults, and enforce with the ec2:MetadataHttpTokens condition key in an SCP; the ec2:RoleDelivery key with value 2.0 additionally rejects API calls made with credentials obtained over IMDSv1. Keep the hop limit at 1 unless containers on the host genuinely need metadata, and in Kubernetes give pods their own identity (EKS Pod Identity or IRSA) instead of the node role. See the EC2 Instance Metadata Service Hardening Guide.
5. Node or default identities doing workload jobs
The mistake: pods use the node's cloud identity, GKE nodes run as the Compute Engine default service account, or Kubernetes pods run as the namespace's default service account with a token mounted they never use.
Why it matters: Google's GKE documentation notes that all workloads on a node share the node's service account, which over-provisions permissions and is unsuitable for multi-tenant clusters. In Kubernetes, every namespace has a default service account and its token is mounted into pods unless automountServiceAccountToken is set to false, so any container escape or remote code execution starts with a valid API credential.
Detect it: list pods whose serviceAccountName is default, any RoleBinding to default, GKE node pools using the Compute Engine default service account, and AKS or EKS workloads relying on the node identity.
The fix: one Kubernetes service account per workload, with Workload Identity Federation for GKE, EKS Pod Identity or IRSA, or Microsoft Entra Workload ID to map it to a cloud identity; set automountServiceAccountToken: false where the pod never calls the API; run node pools with a dedicated least-privilege service account. Pods on the host network bypass GKE Workload Identity Federation and reach the Compute Engine metadata server, so limit host networking. See the Kubernetes Workload Identity Checklist.
6. One identity shared by many workloads or environments
The mistake: a single user-assigned managed identity, IAM role or Google service account serves several applications, or both test and production.
Why it matters: Microsoft's managed identity guidance lists good reasons to share a user-assigned identity, such as replicated servers needing identical permissions, but also points out that anyone who can run code on a resource holding an identity inherits all of its permissions. Sharing across unrelated applications or across environments means the weakest workload sets the blast radius for all of them. It also blurs audit trails: Microsoft recommends a system-assigned identity when logs must show which specific resource acted. This is OWASP NHI9:2025 NHI Reuse and NHI8:2025 Environment Isolation.
Detect it: count resources attached to each user-assigned identity and roles assumed by more than one compute service; flag any identity with access to both production and non-production resources.
The fix: one identity per application per environment, and share only across replicas of the same application. Google's best practices make the same point for federation: a dedicated service account for each application, in the same project as the resources it accesses.
7. Broad trust boundaries
The mistake: roles/iam.workloadIdentityUser is granted to a whole workload identity pool, several clusters with different owners share one GKE project, or several identity providers share one pool.
Why it matters: in GKE, workloads in different clusters of the same project are identified by namespace and service account name, so a backend/back-ksa in an untrusted team's cluster is the same identity as in your production cluster, as Google's documentation states. Google also recommends one provider per pool to avoid subject collisions and warns against granting roles/iam.workloadIdentityUser to the whole pool.
Detect it: search IAM policies for principalSet:// bindings on whole pools or namespaces, list clusters per project with their owning teams, and list pools with more than one provider.
The fix: put clusters with different trust levels in separate projects (or use unique namespace names and IAM conditions that pin the cluster), grant to specific principal:// identities, and keep one provider per pool. For mixed estates, a SPIFFE trust domain per trust boundary gives the same separation; see the Cross-cloud workload identity federation guide.
8. Over-broad permissions on the federated role
The mistake: the team removes the key, celebrates, and leaves the role with AdministratorAccess, subscription Contributor or project Editor.
Why it matters: a one-hour token with administrator rights is enough to create persistence, for example new keys or federated credentials on another identity (ATT&CK T1098.001 Additional Cloud Credentials). Microsoft warns that extra permissions increase the "security blast radius" and that a role able to run code on a resource, or to assign identities, effectively grants that identity's rights.
Detect it: compare granted permissions with used permissions (IAM Access Analyzer unused access findings and last-accessed data in AWS, IAM recommender in Google Cloud, sign-in and activity logs in Entra), and flag any federated identity holding owner-level roles or iam:PassRole on *.
The fix: scope to named resources and actions, separate deploy roles per environment, and add permission boundaries or SCPs so a pipeline role cannot create identities or edit trust policies. See How to find over-privileged Azure managed identities.
9. Uncontrolled change rights over trust configuration
The mistake: many people can create OIDC providers, edit role trust policies, add federated identity credentials to app registrations or change attribute mappings.
Why it matters: the trust rule is now the credential. Microsoft documents that anyone who can create an app registration and add a secret or certificate can also add a federated identity credential to it, and that an incorrect subject is accepted at creation without error. Google recommends limiting iam.googleapis.com/workloadIdentityPoolProviders.update to a few administrators and setting the constraints/iam.workloadIdentityPoolProviders organisation policy to deny all at the root, with exceptions for trusted projects. The Midnight Blizzard breach showed how credentials added to an existing application become persistence.
Detect it: alert on UpdateAssumeRolePolicy and CreateOpenIDConnectProvider in CloudTrail, on federated identity credential changes in Entra audit logs, and on workload identity pool provider updates in Google Cloud Audit Logs.
The fix: manage trust configuration only through reviewed infrastructure as code, restrict who can change it with SCPs, Azure Policy (a deny effect on federatedIdentityCredentials resources is documented) and the Google organisation constraint, and set Entra "Users can register applications" to No where your model allows.
10. No logging that ties tokens back to the workload
The mistake: logs show the role or service account acting, but not which repository, pod or external subject obtained the token.
Why it matters: without attribution, a malicious workflow and a legitimate deploy look the same, and incident responders cannot scope access. Google recommends enabling data access logs for the Security Token Service and IAM APIs in every project that holds pools or federated service accounts, and a unique google.subject mapping so each external identity maps to exactly one subject; impersonated activity then carries the federated subject in serviceAccountDelegationInfo.
Detect it: test it: run a federated job and check whether you can find its repository, workflow and run in the cloud logs within minutes.
The fix: log AssumeRoleWithWebIdentity in CloudTrail with the subject, Entra service principal sign-in logs for federated credentials, and STS logs in Google Cloud; forward them to the SIEM with the CI run ID where you can set a session name or tag. See the Top 10 Cloud Logging Gaps.
Detection and measurement
| Metric | Source | Target |
|---|---|---|
| Federated trust rules with wildcard or organisation-wide subject | Trust policy and provider inventory | Zero for production roles |
| Static keys or secrets on identities that also use federation | Credential reports, key usage, app credentials | Zero after the migration window |
| Instances with IMDSv1 enabled | ImdsV1Enabled metric | Zero |
Pods running as default or on node identity | Cluster inventory | Zero outside system namespaces |
| Identities attached to more than one application or environment | Identity to resource mapping | Falling each quarter |
| Trust configuration changes outside the pipeline | CloudTrail, Entra audit, Cloud Audit Logs | Zero unapproved changes |
Mapping to OWASP
| Mistake | OWASP Non-Human Identities Top 10 2025 | ATT&CK (v20) | NIST SP 800-53 Rev. 5 |
|---|---|---|---|
| 1 Trust rules that check too little | NHI6 Insecure Cloud Deployment Configurations | T1078.004 Cloud Accounts | IA-9, AC-3 |
| 2 Mutable claims | NHI6 Insecure Cloud Deployment Configurations | T1078.004 | IA-9 |
| 3 Static keys kept | NHI7 Long-Lived Secrets, NHI2 Secret Leakage | T1552.001 Credentials In Files | IA-5 |
| 4 Metadata exposed | NHI6 Insecure Cloud Deployment Configurations | T1552.005 Cloud Instance Metadata API | SC-7, CM-6 |
| 5 Node or default identities | NHI5 Overprivileged NHI | T1528 Steal Application Access Token | AC-6 |
| 6 Shared identity | NHI9 NHI Reuse, NHI8 Environment Isolation | T1078.004 | AC-2, AC-6 |
| 7 Broad trust boundaries | NHI8 Environment Isolation | T1199 Trusted Relationship | AC-3, IA-9 |
| 8 Over-broad permissions | NHI5 Overprivileged NHI | T1098.001 Additional Cloud Credentials | AC-6 |
| 9 Uncontrolled trust changes | NHI6 Insecure Cloud Deployment Configurations | T1098.001 | CM-5, AC-2 |
| 10 No attribution | Supports detection across all ten | T1550.001 Application Access Token | AU-2, AU-3 |
In NIST CSF 2.0 these sit under PR.AA-01 (identities and credentials for services are managed) and PR.AA-05 (access permissions are defined, enforced and reviewed with least privilege).
Where to start
- Inventory trust: list every OIDC provider, workload identity pool, federated identity credential and role that trusts an external issuer, with its exact conditions (mistakes 1, 2 and 7).
- Close the multi-tenant gaps first: pin subject and audience on any rule trusting GitHub, GitLab or another shared issuer; these are reachable from the internet by anyone with an account.
- Turn off IMDSv1: watch
MetadataNoToken, require IMDSv2, then set the account default and SCP (mistake 4). Test in a pilot account and roll back per instance withmodify-instance-metadata-optionsif a legacy agent breaks. - Delete the fallbacks: remove static keys on federated identities and block new ones by policy (mistake 3).
- Lock the trust configuration: move it into reviewed infrastructure as code and alert on changes outside it (mistake 9).
- Then right-size: split shared identities and trim permissions using last-used data (mistakes 5, 6 and 8), and confirm every token can be traced to its workload (mistake 10).
Small teams with one cloud and one CI system can do steps 1 to 4 in a sprint. Large estates should run them per landing zone, starting with production accounts that trust a shared CI issuer. See the Top 10 NHI Risks for the wider machine identity picture.
Standards and references
- OWASP: Non-Human Identities Top 10
- AWS: Create a role for OpenID Connect federation
- AWS: Transition to using Instance Metadata Service Version 2
- GitHub Docs: OpenID Connect reference
- Google Cloud: Best practices for using Workload Identity Federation
- Google Cloud: About Workload Identity Federation for GKE
- Microsoft Learn: Important considerations and restrictions for federated identity credentials
- Microsoft Learn: Managed identity best practice recommendations
Related NHI Mgmt Group resources: Cloud Workload Identity Guide · GitHub Actions OIDC Federation Hardening Guide · Kubernetes Service Account Token Guide · Top 10 Service Account Mistakes · Azure Managed Identities Security Guide