Free tech learning for everyone, from basics to pro

Cloud LabsLabsBeginner

Lab: a private, encrypted S3 bucket

In short: Misconfigured S3 buckets have exposed huge amounts of data. In this lab you build a bucket with every public access block on, default encryption, versioning and a policy that refuses plain HTTP, then share one file safely with a time-limited pre-signed URL.

Aim and what you need

The aim is to create object storage that is private by design and to prove it. Amazon S3 (Simple Storage Service) stores files as objects in buckets. Since 2023, new buckets start with Block Public Access on, ACLs (access control lists) disabled and SSE-S3 encryption, but you should still set and check each control explicitly, because older buckets and other tools may differ.

  • An AWS account with an admin IAM user and MFA, plus a budget.
  • AWS CLI v2. A small text file such as notes.txt to upload.
  • A globally unique bucket name, for example <yourname>-lab-private-2026. About 30 minutes; costs are pennies for small files.
Layers protecting the bucketBlock Public Accessall four settings onObject Ownershipbucket owner enforcedDefault encryptionSSE-S3 or SSE-KMSBucket policydeny non-TLS requestsVersioningrecover deleted objects
Figure 1. Each layer covers a different failure: public exposure, stray ACLs, data at rest, data in transit and accidental deletion.

Steps

  1. Create the bucket in your home region.
  2. Turn on all four Block Public Access settings and enforce bucket-owner object ownership.
  3. Set default encryption. This lab uses SSE-S3 (AES-256); to try SSE-KMS, change the algorithm to aws:kms and add a key ID.
  4. Turn on versioning.
  5. Add a bucket policy that denies any request not using TLS (Transport Layer Security).
  6. Upload a file and create a pre-signed URL that expires after five minutes.
B=<yourname>-lab-private-2026
aws s3api create-bucket --bucket $B --region eu-west-2 --create-bucket-configuration LocationConstraint=eu-west-2
aws s3api put-public-access-block --bucket $B --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
aws s3api put-bucket-ownership-controls --bucket $B --ownership-controls 'Rules=[{ObjectOwnership=BucketOwnerEnforced}]'
aws s3api put-bucket-encryption --bucket $B --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"},"BucketKeyEnabled":true}]}'
aws s3api put-bucket-versioning --bucket $B --versioning-configuration Status=Enabled
aws s3api put-bucket-policy --bucket $B --policy '{"Version":"2012-10-17","Statement":[{"Sid":"DenyInsecureTransport","Effect":"Deny","Principal":"*","Action":"s3:*","Resource":["arn:aws:s3:::'$B'","arn:aws:s3:::'$B'/*"],"Condition":{"Bool":{"aws:SecureTransport":"false"}}}]}'
aws s3 cp notes.txt s3://$B/notes.txt
aws s3 presign s3://$B/notes.txt --expires-in 300
Encryption options for S3SSE-S3Keys managed by S3Default for new bucketsNo extra costNo separate key auditSSE-KMSKey in AWS KMSKey policy controls useEach use logged in CloudTrailPer-request KMS charges
Figure 2. SSE-S3 is simple and free; SSE-KMS adds key-level control and auditing at a small cost.

How to verify

Read each setting back and confirm it matches what you set. Then test from the outside. Opening the plain object URL https://<bucket>.s3.eu-west-2.amazonaws.com/notes.txt in a private browser window should return Access Denied. The pre-signed URL should download the file, and the same URL should fail once five minutes have passed. Upload notes.txt again with a change, then list object versions to see both copies. In the console, the bucket should show "Bucket and objects not public", and IAM Access Analyzer for S3 should raise no public-access findings.

aws s3api get-public-access-block --bucket $B
aws s3api get-bucket-encryption --bucket $B
aws s3api get-bucket-versioning --bucket $B
aws s3api get-bucket-policy-status --bucket $B
aws s3api head-object --bucket $B --key notes.txt
aws s3api list-object-versions --bucket $B --prefix notes.txt

Troubleshooting and clean-up

  • BucketAlreadyExists: names are global across all AWS customers. Add something unique.
  • IllegalLocationConstraintException: for us-east-1 leave out the location constraint; for other regions it must match --region.
  • Policy rejected: check the JSON quoting; it is easier to save the policy as policy.json and use --policy file://policy.json.
  • Pre-signed URL denied at once: your computer clock may be wrong, or the URL was copied incompletely.

A versioned bucket cannot be deleted until every version and delete marker is gone. Empty it in the console with the Empty button, which removes all versions, then delete the bucket. If you created a KMS key, schedule its deletion (7 to 30 days) to stop the monthly charge.

aws s3 rm s3://$B --recursive
aws s3api delete-objects --bucket $B --delete "$(aws s3api list-object-versions --bucket $B --query '{Objects: Versions[].{Key:Key,VersionId:VersionId}}' --output json)"
aws s3api delete-objects --bucket $B --delete "$(aws s3api list-object-versions --bucket $B --query '{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}}' --output json)"
aws s3api delete-bucket --bucket $B

Practice

What do the four Block Public Access settings do together?
They block new public ACLs and public bucket policies, ignore any existing public ACLs, and restrict access granted by existing public policies.
How does the bucket policy enforce HTTPS?
It denies every action when the condition aws:SecureTransport is false, so plain HTTP requests are refused.
Why is a pre-signed URL safer than making an object public?
It grants access to one object for a limited time, using the signer’s permissions, and then expires.

Key takeaways

  • Turn on all four Block Public Access settings and disable ACLs.
  • Set default encryption: SSE-S3, or SSE-KMS for key-level control.
  • Deny requests without TLS with a bucket policy.
  • Versioning protects against accidental deletes and overwrites.
  • Share with short-lived pre-signed URLs, never public objects.
Tracks your learning on this device only. See your progress on the learning paths.

About the author

Mohamed Fayas

Founder of FM TecHub. CCNA certified, with a BTEC HND in Network Engineering, and BEng (Hons) in Computer Networking and Cloud Security. He writes practical, plain-English guides on networking, security and cloud.

More about the author

Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.

Stay up to date by email

New notes, tools and security news from FM TecHub, explained simply. Free.

We only use your email to send these updates. See our privacy policy.