AWS Storage Blog

Understanding Amazon S3 Block Public Access

Update (4/27/2023): Amazon S3 now automatically enables S3 Block Public Access and disables S3 access control lists (ACLs) for all new S3 buckets in all AWS Regions.


Storing data securely is a key tenet for every business. Companies balance this against the need to share data where and when it’s needed. Human security review is a powerful tool to help ensure only intended access is granted. Furthermore, it’s stronger when augmented with automated guardrails. A particular type of access, “public access,” is not required for the majority of use cases, to the point that most companies want to detect and block it automatically.

Amazon S3 Block Public Access can help you ensure that your Amazon Simple Storage Service (Amazon S3) buckets don’t allow public access. By default, new buckets, access points, and objects don’t allow public access. However, users can modify bucket policies, access point policies, or object permissions to allow public access. S3 Block Public Access settings override these policies and permissions so that you can limit public access to these resources.

This may have you wondering: what does “public” actually mean? And how does S3 Block Public Access work? In this post, I answer these questions hoping to shed some light on how S3 Block Public Access is helping you protect your S3 buckets from public access.

Note: S3 Block Public Access started rolling out as a default security setting for all new buckets earlier this month.

What is “public,” and how do we check it?

In 2018, we launched S3 Block Public Access—a new single-click security feature to prevent public access to your S3 buckets. In building S3 Block Public Access, we had to figure out what “public” meant for an S3 bucket policy. To do this, we flipped the notion on its head and thought about what made a policy non-public. The answer was trust. If we can help you limit a policy to only those entities that you can trust, then that policy will be non-public.

To make S3 Block Public Access single-click, we infer who you trust from your S3 bucket policy rather than asking you explicitly. Then, we analyze your policy to see if it allows access by anyone who isn’t trusted. It may sound like going in a circle, but the key is that not everything you write in a policy can be trusted in isolation. For example, you can say that you trust account 111122223333. That’s a thing someone owns and controls. But you can’t really say that you trust everybody whose first name is “Andrew.” There are too many Andrews in the world for that, and nobody owns or controls the first name “Andrew.” Trusting them all would be public access. You could say that you trust every “Andrew” in account 111122223333. But there you’re basing the limits of your trust on the account, not on the first name. In isolation, “first name” is simply not a trustworthy concept. By limiting what we consider trustable, we can detect policies that might otherwise lead to public access.

Here’s a summary of how we check your policy for public access:

  1. We examine your policy to figure out who you trust.
  2. We check your policy to see if it allows any access beyond that.

a. If so, then we say your policy is public. If you have S3 Block Public Access turned on, then we block the entire policy update.

b. If not, then everything will work as usual. You won’t notice that S3 Block Public Access is running in the background, keeping your bucket safe from public access.

Putting this into practice

Now let’s see how S3 Block Public Access works on some examples. Let’s suppose you have an S3 bucket named DOC-EXAMPLE-BUCKET with S3 Block Public Access turned on, and you want to attach a new bucket policy. Let’s say you write the following policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-12345678"
        }
      }
    }
  ]
}

This policy grants access to everyone in the Organization o-12345678. That could be hundreds of thousands of users and roles spread across thousands of accounts. Does that mean it’s public? No. It’s not a matter of counting the number of people with potential access. It’s about inferring trust and making sure that access is only granted to those entities. In this case, we infer that you trust the Organization o-12345678, and we check if any access is allowed outside of that Organization. In this case, there’s no additional access, so the policy isn’t public. You can safely attach this policy to your bucket.

Next, let’s suppose you try attaching this more complex policy to the bucket:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
      "Condition": {
        "StringEquals": {
          "aws:username": "Alice",
          "aws:PrincipalTag/department": "finance"
        },
        "StringLike": {
          "aws:PrincipalOrgID": "*"
        },
        "Bool": {
          "aws:MultiFactorAuthPresent": true,
          "aws:SecureTransport": true
        },
        "IpAddress": {
          "aws:VpcSourceIp": "10.20.30.40"
        }
      }
    }
  ]
}

This policy has a whole bunch of restrictions. The caller must be:

  • a user named Alice,
  • tagged with departmentequal to finance,
  • part of an AWS Organization,
  • using two-factor authentication,
  • using a secure (https) connection, and
  • coming from the 10.20.30.40address in the VPC from which they’re calling.

Is this a public policy? Yes. There are many restrictions, but none of them are trustable individually. One way to think about it is to imagine an arbitrary person, John Q Public, who wants to access this bucket. He can create a user named Alice in his own account, attach the appropriate department tag to that user, create an AWS Organization for his account, enable two-factor authentication, and use a secure connection. Finally, he can create his own VPC, assign the IP address 10.20.30.40 to one of his instances, and then make the request from that instance. This policy has many restrictions, but John can control all of them. Because S3 Block Public Access is turned on for this bucket, you can’t attach this policy to the bucket. You must scope the policy down to remove public access before you can attach it.

If you want to know what condition keys, such as aws:SourceVpc and aws:PrincipalOrgId, can be used to avoid public access, then see the S3 Block Public Access documentation. These are the keys that represent trustable entities – things like specific networks, AWS Organizations, accounts, roles, or users. We don’t infer trust based on overly broad notions like “All users in the world named Alice” or “Anybody anywhere who has the department tag equal to finance.”

Finally, let’s look at a policy that uses both allow and deny statements:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
    },
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "* ",
      "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
      "Condition": {
        "StringNotLike": {
          "aws:PrincipalOrgID": "o-12345678"
        }
      }
    }
  ]
}

This policy isn’t public. It might initially look public since it has an allow statement granting access to everybody. But there’s a deny statement that takes away any access outside of Organization o-12345678. In this case, we infer that you trust Organization o-12345678, and we check to see if any other access is allowed. This check requires figuring out precisely how all allow statements and deny statements in a policy interact. For that, we use Automated Reasoning, which you can learn more about in this AWS Security blog post. The basic idea is that we exercise every possible request by using variables instead of specific values.

Conclusion

Hopefully you now have an intuitive understanding of what public access means and how we check for it. You know that when S3 Block Public Access is enabled, AWS will infer who you trust from your policy, and then check that only those trusted identities have access.

If you want to go beyond public/not-public, then you can use IAM Access Analyzer to see which of your buckets (and other resources) are accessible outside your account or Organization. If you want to learn more about how the S3 Block Public Access algorithm works in detail, then we have a peer-reviewed research paper. As a reminder, S3 Block Public Access started rolling out as a default security setting for all new buckets earlier this month. If you haven’t already, then I strongly encourage you turn on S3 Block Public Access for your existing buckets as well. And, if it works for your use case, then you can turn it on for your entire account.