After several years of working with AWS, IAM remains one of the most frequently used services in my daily routine. Yet, despite my familiarity with it, a recent production incident taught me that there’s always more to learn.

AWS bucket policies play a crucial role in restricting access to our buckets. They allow us to specify who can read from and write to them, particularly when dealing with cross-account permissions. Let’s delve into an example of such a bucket policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::***:role/my-app-role"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
            ],
            "Resource": "arn:aws:s3:::stav-bucket/*"
        }
    ]
}

Recently, I needed to replace one of my roles — the same role that possessed read/write permissions for several of my buckets. Initially, I assumed that as long as I retained the role name, everything would remain unaffected. In other words, the role’s ARN within my bucket permissions would remain consistent, thus preserving the permissions even after the role replacement.

However, instead of business as usual, I found myself inundated with “Access Denied” errors from my application, unable to perform operations on the bucket. After inspecting the role (which seemed fine), I pinpointed the issue to the bucket policy. Indeed, something had changed:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "AROAQV5PXLVLG47PAPA3S"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
            ],
            "Resource": "arn:aws:s3:::stav-bucket/*"
        }
    ]
}

The role ARN had been replaced with a cryptic string, leaving me wondering about its origin and significance. Further investigation revealed that this string was, in fact, the unique ID of my role — a string generated by AWS upon role creation.

When saving a policy that references a role’s ARN, AWS internally transforms the ARN into its unique principal ID. While you typically encounter the ARN in the console, AWS internally maps it to its corresponding ID. However, when the mapping breaks — such as when the original role is deleted — the original ARN, represented by its unique ID, resurfaces. Consequently, the role loses its permissions over the bucket.

Even if I were to recreate the “my-app-role” role, its unique ID would differ, and the role would remain unauthorized until I adjusted the bucket policy accordingly.

Reflecting on this incident, I realized its security implications. This mechanism minimizes the risk of privilege escalation through role deletion and recreation. Even if someone were to delete or replace my role, they wouldn’t gain access to my other AWS resources governed by their policies. Each new role iteration comes with a new unique ID, ensuring that my resources do not recognize it as the same role.

While this discovery initially confounded me, I now appreciate the security measures it underscores.

Note: While this blog post focuses on roles, it’s worth noting that AWS users undergo similar processes, generating unique IDs when utilized. Additionally, this phenomenon extends beyond bucket policies to other AWS resource policies, such as trust relationship policies within roles.

LAST UPDATED:

June 23, 2025

Don't miss these stories:

From Rogue OAuth App to Cloud Infrastructure Takeover

How a rogue OAuth app led to a full AWS environment takeover. And the key steps security leaders can take to prevent similar cloud breaches.

CORSLeak: Abusing IAP for Stealthy Data Exfiltration

When people talk about “highly restricted” cloud environments, they usually mean environments with no public IPs, no outbound internet, and strict VPC Service Controls locking everything down.

Defending SaaS & Cloud Workflows: Supply Chain Security Insights with Idan Cohen

From GitHub Actions to SaaS platforms, supply chain threats are growing. Hear Mitiga’s Idan Cohen and Field CISO Brian Contos explore real-world compromises, detection tips, and strategies to strengthen your cloud security.

Inside Mitiga’s Forensic Data Lake: Built for Real-World Cloud Investigations

Most security tools weren’t designed for the scale or complexity of cloud investigations. Mitiga’s Forensic Data Lake was.

Measurements That Matter: What 80% MITRE Cloud ATT&CK Coverage Looks Like

Security vendors often promote “100% MITRE ATT&CK coverage.” The reality is most of those claims reflect endpoint-centric testing, not the attack surfaces organizations rely on most today: Cloud, SaaS, AI, and Identity.

How Threat Actors Used Salesforce Data Loader for Covert API Exfiltration

In recent weeks, a sophisticated threat group has targeted companies using Salesforce’s SaaS platform with a campaign focused on abusing legitimate tools for illicit data theft. Mitiga’s Threat Hunting & Incident Response team, part of Mitiga Labs, investigated one such case and discovered that a compromised Salesforce account was used in conjunction with a “Salesforce Data Loader” application, a legitimate bulk data tool, to facilitate large-scale data exfiltration of sensitive customer data.