Skip to main content


Service Details

Simple Storage Service, or S3, is one of the main managed services used in AWS. S3 is an object storage service that can be used to store and protect any amount of data for any use case, such as data lakes, cloud-native applications, and mobile apps. From a security perspective, it is a common service to be misconfigured, and accidentally expose sensitive data to the internet.

Assessment Notes

  • Ensure the S3 buckets are not publicly exposed
  • Check which principles have read/write access
    • Ensure principle of least privilege is applied
    • Failure to apply least privilege to S3 buckets was a major factor in the CapitalOne breach
  • Review logging for S3 buckets
    • Check status of server access logging for S3
    • Check CloudTrail trails configured for S3
    • Check log destination if enabled
    • If logs not enabled, discuss if it has been considered

Operational Notes

Bucket Policy

An S3 bucket policy is an object that allows you to manage access to specific S3 resources. Permissions can be specified for each resource to allow or deny actions requested by a principal. Bucket policies are an IAM mechanism for controlling access to resources, which are a critical element in securing S3 buckets against unauthorised access.

It is important to note that resources on S3 bucket policies need to have /* appended to the bucket name for the policy to work, for example arn:aws:s3:::bucketname/*.

Example Overly Permissive Bucket Policy

  • Below shows an example bucket policy that is considered an insecure, overly permissive policy
  • The "Principal":"*" entry allows any user with a valid AWS account to read objects
"Version": "2012-10-17",
"Statement": [
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": [
"Resource": [

Example Improved Bucket Policy

  • Below shows an improved bucket policy
  • Only users from the specified account, "arn:aws:iam::111122223333:root" would be able to read objects
"Version": "2012-10-17",
"Statement": [
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "arn:aws:iam::111122223333:root",
"Action": [
"Resource": [

Further bucket policy examples can be found here: Bucket Policy Examples

Access Control Lists

S3 Access Control Lists (ACLs) are a legacy access control mechanism that pre-dates IAM. They can be applied to individual files or objects as well as whole buckets. It is recommended to use a bucket policy rather than ACLs as it provides the means for greater access control.

Determining a Principle's Permissions

Whenever an AWS principle issues a request to an S3 bucket, the authorisation decision depends on the union of all applicable policies. That includes all IAM policies attached to the user or role making the request, the bucket policy if present, and any S3 ACLs that are applied. If conflicts exist between these policies, then the more restrictive policy takes precedence, i.e. an allow policy will not override a deny policy.

  • Default to deny
  • Explicit deny always takes priority over any allow
  • Only allowed if nothing specifies deny and something allows


Encryption of data at rest

Objects stored in S3 buckets should be encrypted at rest using either server-side or client-side encryption:

  • Server-Side Encryption – Amazon S3 encrypts object before saving it to disk and decrypts it when the object is downloaded; encryption is managed by AWS
  • Client-Side Encryption – Encrypt data client-side and upload the encrypted data to S3 bucket; client manages encryption process

Enforce encryption of data in transit

Only encrypted connections over HTTPS should be permitted using the aws:SecureTransport condition on the S3 bucket policies.

Cross-Region Replication

Cross-region replication (CRR) allows you to replicate data between distant AWS Regions, which is sometimes required to meet regulations. CRR enables automatic, asynchronous copying of objects across buckets in different AWS Regions.

  • Data is transmitted over SSL by default, no policy is needed to enforce it
  • Object in a bucket can only be replicated once, globally
  • Source and destination buckets must have versioning enabled
  • Source and destination buckets must be in different AWS regions
  • The IAM principal must have permissions to do the replication
  • Object owner must grant READ and READ_ACP permissions via object ACL, if the source bucket owner is not also the object owner

What is replicated?

  • Anything created after CRR has been configured
  • Anything unencrypted, encrypted with SSE-S3, or encrypted with SSE-KMS key
  • Object metadata
  • Object ACL updates
  • Object tags

What isn't replicated?

  • Anything created before CRR is enabled
  • Objects created with customer-provided (SSE-C) keys
  • Objects created with SSE-KMS, unless specifically enabled
  • Deletes to particular versions of objects


CloudFront is a fast Content Delivery Network (CDN) service that from AWS. It can be configured with an S3 bucket as the content origin.

  • Add a CloudFront distribution for the bucket as the origin and set "restrict bucket access"
  • Edit CloudFront origin to create an origin access identity, or specify an identity to use

S3 buckets can be used to host static websites. In this scenario, CloudFront then acts as a CDN in front of the S3 bucket and so a user is a one region will get served content a lot quicker than going directly to the bucket itself in a different region further away from the user.

CloudTrail can also be used to serve content, such as media downloads, to a larger site that is hosted in a different location.

Pre-Signed URLs

A pre-signed URL is a method that can provide users temporary access to a specific S3 object. Using the URL, a user can either read or write an object. This provides a means to give temporary access to users that do not have an account that can be given the required IAM role. However, this only works on specific objects. If you require access to multiple files within the same bucket, a pre-signed cookie can be used.

aws s3 presign s3://BUCKETNAME/FILENAME.FILE

Signed cookie CloudTrail policy:

CloudFront-Policy=base64 encoded version of the policy statement;
Domain=optional domain name;
Path=/optional directory path;

Useful CLI Commands

# Command structure
aws s3 <Command> [<Arg> ...]

# Create/destroy bucket
aws s3 mb s3://mybucket # creates 'mybucket'
aws s3 rb s3://mybucket # deletes 'mybucket' if empty
aws s3 rb s3://mybucket --force # deletes 'mybucket' and all its contents

# Manage objects
aws s3 ls # list all user owned buckets
aws s3 ls s3://mybucket # list all prefixes for 'mybucket'
aws s3 ls s3://mybucket --recursive # recursively list all prefixes for 'mybucket'

aws s3 cp <LocalPath> <S3Uri> # copy from local disk to S3 bucket
aws s3 cp <S3Uri> <LocalPath> # copy from S3 bucket to local disk
aws s3 cp <S3Uri> <S3Uri> # copy from one S3 to another

aws s3 mv <LocalPath> <S3Uri> # move from local disk to S3 bucket
aws s3 mv <S3Uri> <LocalPath> # move from S3 bucket to local disk
aws s3 mv <S3Uri> <S3Uri> # move from one S3 to another

aws s3 rm <S3Uri> # deletes an object from bucket

# Website configuration
aws s3 website s3://my-bucket/ --index-document index.html --error-document error.html

# Create presign link
aws s3 presign s3://BUCKETNAME/FILENAME.FILE