When trying to configure S3 bucket permissions for backup purposes, the initial policy has several critical issues:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1395161912000",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:PutObject",
"s3:*"
],
"Resource": [
"arn:aws:s3:::bucketname"
]
}
]
}
The wildcard s3:*
is dangerous as it grants full permissions, while the resource ARN is incorrectly formatted for object-level operations.
Here's the corrected version that allows uploads while preventing modifications:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::bucketname"
]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::bucketname/*"
],
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
Three critical components make this work:
- The bucket ARN format differs from object ARN (note the
/*
suffix) - Explicitly denying DeleteObject prevents overwrites
- The ACL condition ensures proper ownership
For versioned buckets, add these statements:
{
"Effect": "Deny",
"Action": [
"s3:DeleteObjectVersion",
"s3:PutObjectVersionAcl"
],
"Resource": ["arn:aws:s3:::bucketname/*"]
}
Use AWS CLI to verify:
aws s3 cp testfile.txt s3://bucketname/ --acl bucket-owner-full-control
aws s3 rm s3://bucketname/testfile.txt # This should fail
- Missing bucket listing permission causes "Access Denied" for directory views
- Incorrect resource ARNs (missing
/*
for objects) break uploads - Conflicting statements in policy hierarchy can produce unexpected results
When setting up backup solutions using AWS S3, we often need to grant users upload capabilities while preventing them from modifying or deleting existing objects. A common mistake is assuming the basic PutObject
permission alone will suffice.
The original policy has several problems:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:PutObject",
"s3:*"
],
"Resource": [
"arn:aws:s3:::bucketname"
]
}
]
}
1. The s3:*
wildcard contradicts the security requirement
2. Missing object-level resource ARN specification
3. No explicit deny for delete operations
Here's the proper way to implement upload-only access:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
},
{
"Effect": "Deny",
"Action": [
"s3:DeleteObject",
"s3:PutObjectAcl",
"s3:PutObjectVersionAcl"
],
"Resource": [
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
1. Always specify both bucket and object resources separately
2. Explicitly deny dangerous operations rather than relying on absence of allow
3. Consider adding versioning to prevent accidental overwrites
4. For CLI testing, use:
aws s3 cp testfile.txt s3://your-bucket-name/ --profile upload-only-user
For additional security, you can implement pre-signed URLs with expiration:
import boto3
s3 = boto3.client('s3')
url = s3.generate_presigned_url(
ClientMethod='put_object',
Params={'Bucket': 'your-bucket', 'Key': 'object-key'},
ExpiresIn=3600
)
If you still get access denied errors:
- Verify bucket policy doesn't conflict with IAM policy
- Check for explicit denies in permission boundaries
- Ensure proper resource ARN formatting (trailing /* for objects)
- Test with AWS Policy Simulator first