S3 ACLs and bucket policies are two distinct mechanisms for controlling access to your S3 buckets and objects, and understanding their differences is key to implementing robust security.
Let’s see them in action. Imagine you have a public-facing website hosted on S3, and you want to grant read-only access to all objects within a specific bucket, my-public-website-bucket.
First, the bucket policy. This is a JSON document attached directly to the bucket.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-public-website-bucket/*"
}
]
}
This policy grants s3:GetObject permission to everyone ("Principal": "*") for all objects ("Resource": "arn:aws:s3:::my-public-website-bucket/*") within my-public-website-bucket. When a user tries to access an object, S3 evaluates this policy first.
Now, let’s consider a scenario where you have a private bucket, my-sensitive-data-bucket, and you want to allow a specific AWS account, 111122223333, to upload objects, but not delete them. You can use a bucket policy for this too:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAccountUploadOnly",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:root"
},
"Action": [
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-sensitive-data-bucket/*",
"arn:aws:s3:::my-sensitive-data-bucket"
]
}
]
}
Here, the Principal is explicitly set to the root of account 111122223333. The Action allows s3:PutObject and s3:ListBucket. Notice the Resource has two entries: one for the objects within the bucket (/*) and one for the bucket itself (my-sensitive-data-bucket). s3:ListBucket requires access to the bucket ARN, not just the objects.
ACLs, on the other hand, are attached to individual buckets and objects. They are XML-based and more granular, allowing you to grant specific permissions to AWS accounts or predefined groups.
Let’s say you have a bucket my-shared-bucket and you want to grant read-only access to another AWS account, 444455556666. You could do this via the bucket’s ACL:
<AccessControlList>
<Grant>
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
<ID>a1b2c3d4e5f60123456789abcdef0123456789abcdef0123456789abcdef0123456789</ID>
</Grantee>
<Permission>READ</Permission>
</Grant>
<Owner>
<ID>your-canonical-user-id</ID>
</Owner>
</AccessControlList>
You would need the canonical user ID of the grantee account to specify them. You can obtain this ID using the AWS CLI: aws s3api list-buckets --query Owner.ID --output text. This ACL grants READ permission to the specified canonical user.
The primary problem S3 ACLs and bucket policies solve is defining who can do what to your S3 data. This is fundamental for security, compliance, and enabling collaboration. Bucket policies are generally preferred for most use cases because they offer a more centralized and programmatic way to manage permissions, especially for complex scenarios involving multiple accounts or fine-grained control over actions. ACLs, while still functional, are considered a legacy access control mechanism and are often more cumbersome to manage at scale. For instance, if you want to grant public read access to all objects in a bucket, a bucket policy is the idiomatic and recommended approach. ACLs can also be overwritten by bucket policies, so understanding the precedence is critical.
The one thing most people don’t realize is that when you enable "Block Public Access" settings on a bucket, these settings override any ACLs or bucket policies that would grant public access. This is a powerful safety net. Even if you inadvertently configure a policy or ACL to make your data public, Block Public Access will prevent it. This is why it’s generally recommended to keep Block Public Access enabled unless you have a very specific, well-understood need for public access.
The next concept you’ll likely grapple with is IAM policies and how they interact with S3 bucket policies and ACLs, creating a layered access control model.