# S3 Security, Encryption, and Access Controls

> Chapter 2 of the Courseiva AWS-DEVELOPER-ASSOCIATE curriculum — https://courseiva.com/learn/aws-developer-associate/s3-security-and-encryption

**Official objective:** Domain 1 — Implement S3 bucket policies, ACLs, server-side encryption, and presigned URLs.

## Introduction

How do you stop the wrong people from reading or deleting files in your cloud storage? That is the defining problem S3 Security, Encryption, and Access Controls solve. For the DVA-C02 exam, you need to know how to lock down S3 buckets with policies, access control lists (ACLs), encryption, and presigned URLs — and which method to use in which situation.

## The Apartment Building Mailbox Analogy

You and your housemates just moved into a huge apartment building. The building manager gives each flat a small, locked mailbox in the lobby. Your mailbox is for you — anyone can shove a letter into the slot, but only you have the key to open it and read the contents. Now, the building manager also has a master key that can open every single mailbox. Why? In case the fire department needs to access a critical document during an emergency, the manager can let them in without having to track you down.

This is exactly how S3 works. The mailbox is an S3 bucket — a container for your files (objects). The locked mailbox door is server-side encryption: the file is encrypted at rest so nobody can read its contents even if they get physical access to the hard drive. The key you hold is equivalent to an AWS Key Management Service (KMS) key managed by you. The building manager's master key is analogous to an AWS managed key — AWS holds the key but uses it only when you tell it to.

Now imagine you want to give your friend from the building next door temporary access to drop off a package in your mailbox while you’re on holiday. You can generate a special, time-limited code (a presigned URL) that the friend can use once, then it expires. The code gives them write-only access to your mailbox slot for exactly 24 hours. This is precisely what presigned URLs do — they grant temporary, permission-limited access to a specific S3 object without needing the friend to have their own AWS account or permanent permissions.

## Core explanation

S3 (Simple Storage Service) is Amazon’s object storage service. You store files called 'objects' inside containers called 'buckets'. By default, everything is private. The second you upload a file, nobody except the bucket owner can read it. That is a good baseline, but in real life you need to share data with users, applications, and other AWS services. That is where security, encryption, and access controls come in.

Let us start with the first layer: access controls. There are two main types: bucket policies and Access Control Lists (ACLs). A bucket policy is a JSON document (a structured text file) that you attach to the whole bucket. It says things like 'allow anyone from this AWS account to read objects in this bucket' or 'deny all write access from outside this company'. ACLs are an older, simpler system. You can attach an ACL to a bucket or to an individual object. It grants basic permissions like READ, WRITE, or FULL_CONTROL to specific AWS accounts or to the public. Bucket policies are the modern, recommended way. AWS itself recommends you disable ACLs unless you have a legacy reason to use them.

Now, encryption. Encryption scrambles your data so that only someone with the correct decryption key can read it. S3 offers three types of server-side encryption (SSE). SSE means the encryption happens on the AWS server side before the data is saved to disk.

- SSE-S3: Amazon S3 manages the encryption keys. It is the simplest to set up — you just tick a box. For most use cases, this is enough.
- SSE-KMS: You use AWS Key Management Service (KMS) to manage the keys yourself. You can create, rotate, and control who can use the keys. This gives you more control and is required for compliance with regulations like HIPAA.
- SSE-C: You provide your own encryption key. AWS uses it to encrypt the object, then immediately discards the key. You have to supply the same key every time you want to read the object. This is for maximum control but also maximum complexity.

Next, presigned URLs. Imagine you have a private file in S3 — a contract PDF — and you want to let your client download it for exactly one hour. You do not want to make the bucket public. You do not want to give the client an AWS account. The solution is a presigned URL. You, the bucket owner, generate a URL that includes your credentials (a signature) and an expiration time. You give that URL to the client. For the next hour, anyone with that URL can read (or write, depending on how you generated it) the object. After the hour, the URL expires and is useless. This is extremely useful for things like temporary file uploads or secure download links.

Finally, there is also bucket-level encryption settings. You can configure a default encryption behaviour on a bucket so that every object uploaded is automatically encrypted with SSE-S3 or SSE-KMS. This saves you from having to set encryption on every single upload.

Let us also talk about IAM (Identity and Access Management). IAM is the AWS service that lets you create users, groups, and roles. An IAM role is like a 'container of permissions' that an AWS service (like an EC2 instance) can assume temporarily. If an EC2 instance needs to read from an S3 bucket, you do not store a password on the instance — you attach an IAM role to the EC2 instance, and the role gives it permission. This is called the 'principle of least privilege': give only the permissions necessary for the task, nothing more.

To sum up, S3 security works in layers: bucket policy, IAM policies, encryption settings, and presigned URLs for temporary access. You can combine them. For example, you can have a bucket policy that denies all public access, and then use a presigned URL to give temporary access to a specific file. The exam will expect you to know which layer solves which problem and how they interact.

## Real-world context

Let us say you are the lead developer at a startup called 'CloudPhotos'. Your app lets users upload photos and share them with their friends. Users upload photos; the app stores them in S3. Friends get a link to view the photo. Here is exactly how you would implement security and access controls step by step.

First, you create an S3 bucket called cloudphotos-user-uploads. You apply a bucket policy that denies all public access. You do this because you never want a user’s private photos to be visible to the whole internet. You also enable 'Block Public Access' settings on the bucket — these are bucket-level settings that override any policy that tries to make objects public.

Second, you set up default encryption on the bucket using SSE-S3. Now every photo uploaded is automatically encrypted. Even if someone managed to get the physical backup of the hard drive, they cannot view the photos without the decryption key, which AWS manages securely.

Third, you create an IAM role called PhotoUploadRole. This role has a policy that allows an EC2 instance (your app server) to read and write to the bucket. You launch your app server with this role attached. The app server can now upload photos using the role — the authentication is handled automatically, no hardcoded passwords.

Fourth, when a user shares a photo with a friend, the app server receives a request. The app server generates a presigned URL for that specific photo object. The presigned URL has an expiration of 15 minutes. The app sends this URL to the friend’s browser. The friend can view the photo for 15 minutes. After that, the URL expires. This way, you never expose your bucket policies or make the photo public.

Fifth, you set up S3 server access logging. This records every request made to the bucket — who accessed it, when, and from which IP address. If a breach occurs, you can trace it.

Sixth, you configure lifecycle policies to move older photos to S3 Glacier (cheaper, long-term storage) after 90 days. This does not affect security — it just moves the objects to different storage tiers.

In your day-to-day job, you would also use tools like the AWS Management Console (a web interface), AWS CLI (command line), or SDKs (software development kits) to manage these settings. You would write bucket policies as JSON files, apply them via the console, and test them using the IAM policy simulator.

The key point: every decision is about minimising risk. You want to let your application scale to millions of users without exposing data to attackers. S3 security gives you the building blocks to do that.

## Exam focus

The DVA-C02 exam loves to test S3 security and encryption. Expect around 8-12 questions that involve bucket policies, ACLs, encryption, or presigned URLs. The questions often present a scenario and ask you to choose the most secure or most cost-effective solution.

Here are the exact concepts they test:

- Bucket policies vs IAM policies: Which takes precedence? The rule is: if an IAM policy grants access but a bucket policy explicitly denies it, the DENY wins. The most restrictive policy always applies. The exam loves this nuance.
- Encryption types: They ask you to differentiate between SSE-S3, SSE-KMS, and SSE-C. You must know: SSE-S3 is for simplicity, SSE-KMS for audit trails and key control, SSE-C for when you want to manage your own keys.
- Bucket policy conditions: They love the 'aws:SourceIp' condition — a bucket policy that restricts access to only requests from a specific IP range. Also 'aws:SecureTransport' — requires HTTPS.
- Presigned URL generation: You must know you need IAM credentials (an access key and secret key) to generate a presigned URL. You must set an expiration time. You can generate a URL for GET or PUT operations.
- Cross-account access: They present a scenario where an application in Account A needs to read data in Account B’s bucket. The correct answer typically involves a bucket policy in Account B that grants access to the IAM role in Account A, then a role assumption in Account A.
- Object ownership and ACLs: If you upload an object to a bucket owned by another account, the bucket owner does not automatically own that object. This causes problems if the bucket policy grants access to the bucket owner. The fix is to enable S3 Object Ownership (bucket owner enforced) on the bucket.
- Server-side encryption and presigned URLs: You need to know that if a bucket has default SSE-KMS encryption, a presigned URL generated with the wrong KMS key will fail.

Common traps:

- Trap 1: A question says 'Which method allows a user to upload a file for 60 seconds without an AWS account?' The answer is 'presigned URL' — not 'make the bucket public'.
- Trap 2: They ask 'Which encryption type provides the most control?' The inexperienced candidate picks SSE-C (your own key). But the correct answer might be SSE-KMS if the question values audit trails and automatic key rotation.
- Trap 3: They give a bucket policy with a deny statement. They then ask if an IAM policy that grants access will work. The answer is no — explicit deny always overrides.
- Trap 4: They mention ACLs. The modern best practice is to disable ACLs, but the exam still tests them. Know the difference between 'object ACL' and 'bucket ACL'.
- Trap 5: They ask about 'pre-signed' (note spelling) versus 'pre-signed' — the exam uses 'presigned'. Not a real trap, but you need to recognise the term.

Key definitions to memorise:

- Bucket policy: A JSON policy attached to a bucket that defines who can access it.
- ACL: An older, less flexible permission method. You grant permissions to AWS accounts or groups (e.g., AllUsers, AuthenticatedUsers).
- SSE: Server-side encryption — encryption done by AWS before writing to disk.
- SSE-S3: Amazon-managed keys.
- SSE-KMS: AWS KMS-managed keys.
- SSE-C: Customer-provided keys.
- Presigned URL: A time-limited URL that contains your credentials, allowing temporary access to an S3 object.
- IAM role: A set of permissions that an AWS service can assume.
- Object ownership: If you upload to another account’s bucket, the uploader owns the object unless 'bucket owner enforced' is on.

## Step by step

1. **Create the S3 bucket** — Decide the bucket name (globally unique) and region. During creation, choose 'Block all public access' to start from a secure state. This prevents any accidental exposure.
2. **Enable default encryption** — In the bucket properties, set default encryption. Choose SSE-S3 (simplest) or SSE-KMS (more control). Every new object uploaded will be automatically encrypted without any extra code.
3. **Attach a bucket policy** — Write a JSON bucket policy that defines who can do what. For example, grant read-only access to a specific IAM role from another account. Use conditions like 'aws:SourceIp' to restrict based on location.
4. **Create an IAM role for your application** — Create an IAM role with a policy that allows the required S3 actions (e.g., s3:GetObject, s3:PutObject). Attach this role to your EC2 instance or Lambda function. The application can now access S3 securely without storing credentials.
5. **Generate a presigned URL for temporary access** — When a user needs to share a file, your application uses the AWS SDK (e.g., boto3 for Python) to generate a presigned URL. Set an expiration time — minimum seconds, maximum 7 days. Give the URL to the recipient. The URL includes a signature that proves you authorised it.
6. **Test and audit access** — Enable S3 server access logging to capture all requests. Use AWS CloudTrail to log management events (e.g., bucket policy changes). This allows you to identify security incidents and verify that permissions work as intended.

## Comparisons

### Bucket Policy vs IAM Policy

**Bucket Policy:**
- Attached to the S3 bucket itself
- Can grant access to principals from other AWS accounts
- Overrides IAM policies only when denying — explicit deny wins

**IAM Policy:**
- Attached to an IAM user, group, or role
- Only controls entities within the same AWS account
- Must be combined with bucket policy for cross-account access

### SSE-S3 vs SSE-KMS

**SSE-S3:**
- Amazon manages the encryption keys entirely
- No additional cost for key management
- No audit trail of who used the key

**SSE-KMS:**
- You manage keys with AWS KMS
- Costs based on KMS key usage (calls)
- Provides auditing via CloudTrail for every decryption attempt

### Presigned URL (GET) vs Presigned URL (PUT)

**Presigned URL (GET):**
- Grants read-only access to an object
- Used to share a download link with a user
- Response returns the object data

**Presigned URL (PUT):**
- Grants write access to upload an object
- Used to allow a user to upload a file to S3
- Response is empty (success code)

### ACL vs Bucket Policy

**ACL:**
- Legacy method, less flexible
- Grants permissions only to AWS accounts or predefined groups
- Cannot use conditions like 'aws:SourceIp'

**Bucket Policy:**
- Modern, recommended method
- Can grant permissions to any IAM principal across accounts
- Supports conditions (IP, MFA, time-based) for fine-grained control

## Common misconceptions

- **Misconception:** If I set an S3 bucket to 'block all public access', my own IAM users can still read objects via the console. **Reality:** No — 'block all public access' blocks all public traffic, but if you are a user within the same AWS account and have explicit IAM permissions, you can still access it. The block applies only to 'public' requests (requests without AWS credentials). (The term 'public access' is confusing. Beginners think it means 'access from any source', including themselves. AWS defines 'public' as 'requests not signed with AWS credentials'. Your own IAM user is signing requests, so you are not public.)
- **Misconception:** If I use SSE-KMS, I can generate a presigned URL for any user, and they will be able to decrypt the object. **Reality:** No — a presigned URL gives access to the S3 object, but if the object is encrypted with SSE-KMS, the person using the URL also needs permission to use the KMS key (kms:Decrypt). Otherwise, the download will fail with an access denied error. (People forget that encryption keys are a separate layer of security. Just because you have permission to read the object does not mean you have permission to decrypt it.)
- **Misconception:** ACLs are the best way to give another AWS account access to your S3 bucket. **Reality:** No — the modern recommended way is to use a bucket policy that grants access to the other account's IAM role. ACLs are legacy, less flexible, and harder to audit. AWS recommends disabling ACLs entirely. (ACLs were the original method, so older tutorials and courses mention them. Beginners assume 'older means well-established' rather than 'older means deprecated'.)
- **Misconception:** A bucket policy that allows access from any AWS account means any user on the internet can access the bucket. **Reality:** No — a bucket policy that allows access from 'AWS:Principal': '*' means any AWS account (not any anonymous internet user). To allow anonymous access, you need to set the 'Effect': 'Allow' and 'Principal': '*' with no condition. The exam distinguishes between 'any AWS user' and 'any internet user'. (The word 'anyone' is ambiguous. In AWS, 'Principal' is a specific IAM term that refers to AWS entities (users, roles, accounts). Anonymous internet users are not AWS principals.)

## Key takeaways

- The most restrictive policy always wins: if a bucket policy denies access, no IAM policy can override it.
- SSE-S3 is the simplest encryption: all data is encrypted with Amazon-managed keys at no extra cost.
- Presigned URLs grant temporary access to S3 objects without making them public and without requiring the recipient to have AWS credentials.
- Always enable 'Block Public Access' on buckets unless you have a specific reason to allow public access — it is a default security best practice.
- If a bucket has default SSE-KMS encryption, any presigned URL must also include permission to use the correct KMS key (kms:Decrypt).
- Use bucket policies for cross-account access; use IAM policies for granting access to users or roles in your own account.
- Disable ACLs on new buckets to avoid confusion — AWS now recommends using bucket policies exclusively.
- Server-side encryption with keys you manage (SSE-KMS) gives you audit trails via AWS CloudTrail and automatic key rotation.

## FAQ

**What is the difference between a bucket policy and an IAM policy?**

A bucket policy is attached to the S3 bucket itself and controls access from any entity (including other AWS accounts). An IAM policy is attached to a user, group, or role and controls what that entity can do. Both must grant permission for an action to occur — if one denies, the action is denied.

**Can I use a presigned URL to allow a user to upload a file?**

Yes. You generate a presigned URL with HTTP method PUT. The URL gives the recipient write access to a specific S3 object for a limited time. The user does not need an AWS account.

**What happens if someone tries to use a presigned URL after it expires?**

S3 returns an 'Access Denied' error. The URL is only valid until the expiration timestamp. This is why you must choose an appropriate expiration time — not too short (user can't finish upload) and not too long (higher risk of interception).

**Does S3 automatically encrypt data at rest?**

No, not by default. You must enable encryption. You can set default encryption on a bucket so that all newly uploaded objects are encrypted. You can also encrypt individual objects during upload by specifying an SSE header.

**What is S3 Object Ownership and why does it matter?**

S3 Object Ownership controls who owns objects in a bucket. By default, if an AWS account uploads an object to your bucket, that account owns it, and your bucket policy may not apply. Enabling 'Bucket owner enforced' (through Object Ownership) makes the bucket owner the sole owner of all objects, simplifying permission management.

**I see the term 'pre-signed' written as 'pre-signed' and 'presigned' — which is correct for the DVA-C02?**

AWS documentation uses 'presigned' (no hyphen). The exam will use 'presigned URL'. Be consistent in your notes.

---

Interactive version with quiz and diagrams: https://courseiva.com/learn/aws-developer-associate/s3-security-and-encryption
