Amazon S3 is the single most heavily tested service on the AWS Certified Security – Specialty (SCS-C02) exam. It shows up in the Data Protection domain (encryption, key management), in Identity and Access Management (bucket policies, cross-account access, condition keys), in Infrastructure Security (VPC endpoints, network restrictions), and in Detection (data events, access logging, Macie). If you can reason confidently about who can read an object, how it’s encrypted, and how you’d prove a bucket is not public, you have already answered a large fraction of the questions the exam will throw at you.
The problem is that S3 access control is layered. A single GetObject request passes through identity policies, bucket policies, Block Public Access, VPC endpoint policies, Organizations Service Control Policies, and — for legacy setups — ACLs. The exam loves scenarios where two of these layers disagree, and expects you to know which one wins. This guide builds that mental model from the request up, then works through encryption, network isolation, temporary access, and exposure detection with the policy JSON and CLI commands you’ll actually be tested on. Pair it with the KMS and data protection guide for the encryption fundamentals and the IAM deep dive for the policy-evaluation logic that gates everything below.
Where S3 Sits in the SCS-C02 Blueprint
S3 is not its own domain — it’s the connective tissue that runs through nearly all of them. Understanding which domain a question is really testing helps you pick the right control.
| Concern | SCS-C02 domain | Primary controls |
|---|---|---|
| Who can access an object | IAM (20%) | Identity policies, bucket policies, ACLs, access points |
| Is the bucket public | IAM / Infrastructure | Block Public Access, Access Analyzer, bucket policies |
| Data encrypted at rest | Data Protection (18%) | SSE-S3, SSE-KMS, DSSE-KMS, bucket keys, default encryption |
| Data encrypted in transit | Data Protection | aws:SecureTransport, TLS enforcement |
| Access limited to a network | Infrastructure Security (18%) | Gateway VPC endpoints, aws:sourceVpce, aws:sourceVpc |
| Proving/monitoring exposure | Detection (16%) | CloudTrail data events, server access logs, Macie, Config |
Keep this table in your head. When a scenario says “an object was read from the public internet,” it’s an IAM + Block Public Access question. When it says “we must guarantee data never traverses the internet,” it’s a VPC endpoint question. The service is the same; the correct answer depends on the domain lens.
How an S3 Request Is Authorized
Every request to S3 is evaluated against all applicable policies, and the result follows AWS’s standard policy-evaluation logic: an explicit Deny anywhere always wins, and in the absence of a deny the request must be allowed by at least one policy that applies to the principal.
For a same-account request, the principal only needs an Allow from either an identity (IAM) policy or the bucket policy — access is the union of the two, minus any explicit deny (including Block Public Access and any SCP).
For a cross-account request, the rules are stricter: the request must be allowed by an identity policy in the caller’s account and by the bucket policy (or an ACL) in the resource’s account. Both sides must say yes.
The full order of evaluation, any one of which can veto:
| Layer | Scope | Can it deny? | Notes |
|---|---|---|---|
| Organizations SCP | All principals in the account | Yes | A guardrail; never grants, only limits |
| Block Public Access | Bucket + account | Yes | Overrides public bucket policies/ACLs |
| VPC endpoint policy | Requests via that endpoint | Yes | Limits what the endpoint can reach |
| Bucket policy | The bucket | Yes (and grants) | Resource-based policy |
| IAM identity policy | The principal | Yes (and grants) | Attached to user/role |
| S3 ACL (legacy) | Bucket/object | Grants only | Disable with Object Ownership |
The exam’s favourite trick is a bucket policy that allows public read while Block Public Access is enabled — the object is not accessible, because Block Public Access is an explicit override that ignores the permissive policy. Learn to spot the override rather than reading the bucket policy in isolation.
Block Public Access: The Exam’s Favourite Safety Net
S3 Block Public Access (BPA) is a set of four independent settings that can be applied at the account level and the bucket level. When enabled, they neutralise any configuration that would otherwise expose data publicly. Since April 2023, new buckets have BPA on and ACLs disabled by default.
| Setting | What it blocks |
|---|---|
BlockPublicAcls | Rejects PUTs of new public ACLs |
IgnorePublicAcls | Ignores existing public ACLs |
BlockPublicPolicy | Rejects bucket policies that grant public access |
RestrictPublicBuckets | Limits access on already-public buckets to AWS service principals and authorized users |
Enable all four at the account level as a baseline — this is almost always the “most secure” answer on the exam:
aws s3control put-public-access-block \
--account-id 111122223333 \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
Two exam-critical facts: account-level BPA overrides any bucket-level setting that is more permissive, and BPA is what you reach for when the question asks how to prevent future public exposure across an entire organization — often combined with an SCP that denies s3:PutBucketPublicAccessBlock so nobody can turn it off. It does not encrypt anything or restrict cross-account access between trusted accounts; it only governs public access.
Bucket Policies vs. IAM Policies vs. ACLs
Three mechanisms can grant S3 access. Knowing when each applies is a recurring question.
- IAM identity policies attach to a principal (user/role) and are best when you manage access centrally per team. Use them when the who is in your account and you want the permission to travel with the principal.
- Bucket policies attach to the bucket and are best when the what is fixed and you want to state rules about it — enforce encryption, restrict to a VPC, grant cross-account access, or deny non-TLS requests. Bucket policies are also the only way to apply a
Denythat no per-principal policy can escape. - ACLs are the legacy mechanism and AWS now recommends disabling them entirely via Object Ownership → Bucket owner enforced. When ACLs are disabled, the bucket owner automatically owns every object and access is controlled purely by policies — a cleaner model the exam treats as the modern best practice.
A canonical bucket policy that denies any request not using TLS (an exam staple for “encryption in transit”):
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::secure-bucket",
"arn:aws:s3:::secure-bucket/*"
],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}]
}
Cross-Account Access Patterns
Cross-account S3 is where candidates lose points. There are three defensible patterns, and the exam expects you to match them to constraints:
- Bucket policy + caller IAM policy. The resource account’s bucket policy names the external account (or role) as
Principal; the caller’s IAM policy allows the S3 action. Simple, but the object owner problem bites: if the external account writes objects, it may own them unless you enforcebucket-owner-full-controlor, better, disable ACLs with Bucket owner enforced so the bucket owner always owns new objects. - Assume a role in the resource account. The caller assumes an IAM role via STS and acts as a local principal. This sidesteps object-ownership issues entirely and is the cleanest answer when the external party needs broad, ongoing access.
- S3 Access Points. Create a named access point with its own policy for a specific application or account, keeping the bucket policy small. Ideal when many teams share one bucket.
When a scenario stresses “the bucket owner must always be able to read objects written by another account,” the answer is disable ACLs (Bucket owner enforced) or require the bucket-owner-full-control canned ACL on upload — not a new IAM role.
Encryption at Rest: SSE-S3, SSE-KMS, and DSSE-KMS
As of January 2023, all new objects are encrypted at rest by default with at least SSE-S3. The exam still expects you to distinguish the options and know how to enforce the one you want.
| Method | Key management | Audit per request | When the exam wants it |
|---|---|---|---|
SSE-S3 (AES256) | AWS-owned keys | No | Default, low overhead, no per-key control |
SSE-KMS (aws:kms) | Customer-managed KMS key | Yes (CloudTrail) | Key policies, rotation, per-request audit, cross-account key control |
DSSE-KMS (aws:kms:dsse) | Customer-managed, double-layer | Yes | Regulatory “two layers of encryption” requirements |
| SSE-C | Customer-provided key | No | Customer holds keys; AWS never stores them |
Set default encryption so every object lands encrypted with your chosen key, and enable S3 Bucket Keys with SSE-KMS to cut KMS GenerateDataKey calls (and cost) dramatically:
aws s3api put-bucket-encryption \
--bucket secure-bucket \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:111122223333:key/abcd-1234"
},
"BucketKeyEnabled": true
}]
}'
Default encryption ensures objects are encrypted but does not reject an upload that specifies a weaker method. To force a specific key, add a bucket policy that denies uploads without the right header:
{
"Sid": "RequireKmsKey",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::secure-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
}
Remember the cross-account subtlety with SSE-KMS: a principal reading a KMS-encrypted object needs kms:Decrypt on the key, granted by the key policy (and/or a grant), not just s3:GetObject. A very common exam wrong-answer is “grant S3 permissions” when the real fix is a KMS key policy allowing the external principal to decrypt. The KMS and data protection guide covers key policies versus grants in depth.
Restricting a Bucket to a VPC
Infrastructure Security questions often require that data “never traverses the public internet” or is “only reachable from our VPC.” The answer is an S3 gateway VPC endpoint plus condition keys that pin access to that endpoint or VPC.
- Create a gateway endpoint for S3 (free, route-table based) so traffic to S3 stays on the AWS network.
- Add an endpoint policy to limit which buckets the endpoint can reach.
- Add a bucket policy that denies any request not arriving through the approved endpoint or VPC.
{
"Sid": "AccessOnlyViaVPCE",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::secure-bucket",
"arn:aws:s3:::secure-bucket/*"
],
"Condition": {
"StringNotEquals": { "aws:sourceVpce": "vpce-0abc123def456" }
}
}
Know the difference between the condition keys: aws:sourceVpce pins to a specific endpoint ID, while aws:sourceVpc pins to a whole VPC. Use aws:SourceIp only for public-internet CIDR restrictions — it does not apply to requests coming through a VPC endpoint. See the infrastructure security guide for how endpoints fit into broader network segmentation.
Access Points and Presigned URLs
S3 Access Points are named network endpoints with their own policies, attached to a single bucket. Instead of one sprawling bucket policy, each application gets an access point scoped to its prefix and network. Multi-Region Access Points extend this across regions with automatic routing and failover. Reach for access points when a scenario describes “many teams/apps sharing one bucket, each needing least-privilege access.”
Presigned URLs grant time-limited access to a specific object using the credentials of the signer. They’re the right answer for “let an unauthenticated user upload/download one object without making the bucket public.” Two facts the exam checks: the URL inherits the permissions of the principal who signed it (a presigned URL from an over-privileged role is dangerous), and its lifetime is capped by the signer’s credential lifetime — up to 7 days when signed with an IAM user’s long-term keys, but only as long as the session token lasts when signed with temporary role credentials.
Detecting and Proving Exposure
The Detection domain expects you to know how to find and monitor S3 risk, not just prevent it:
- S3 server access logs and CloudTrail data events record object-level
GetObject/PutObjectactivity — data events are off by default and must be explicitly enabled (and cost extra). This is your answer for “who read this object?” - IAM Access Analyzer continuously flags buckets shared outside your account/organization — the answer for “identify buckets accessible externally.”
- AWS Config managed rules such as
s3-bucket-public-read-prohibitedands3-bucket-server-side-encryption-enabledprovide continuous compliance checks and can auto-remediate. - Amazon Macie discovers and classifies sensitive data (PII, credentials) inside buckets — the answer for “find where sensitive data lives.”
Match the verb in the question to the service: detect external sharing → Access Analyzer; classify sensitive content → Macie; continuous config compliance → Config; object-level audit trail → CloudTrail data events. The logging and monitoring guide expands on the trail and log-analysis side.
Common Exam Scenarios, Mapped
| Scenario | Correct control |
|---|---|
| Guarantee no bucket in the org can be made public | Account-level Block Public Access + SCP denying its removal |
| Bucket owner must read objects uploaded by another account | Disable ACLs (Bucket owner enforced) or require bucket-owner-full-control |
| Data must never leave the AWS network | S3 gateway VPC endpoint + bucket policy on aws:sourceVpce |
| Reject any non-HTTPS request | Bucket policy denying aws:SecureTransport=false |
| Force all objects to use a specific CMK | Default encryption + policy denying wrong s3:x-amz-server-side-encryption |
| External account can’t decrypt KMS-encrypted objects | Add kms:Decrypt to the KMS key policy |
| Give a partner one-time download without a public bucket | Presigned URL signed by a least-privilege role |
| Find buckets shared outside the account | IAM Access Analyzer |
| Discover PII stored in S3 | Amazon Macie |
Hands-On: Locking Down a Bucket
A realistic lab flow that mirrors exam tasks — enforce encryption, block public access, and require TLS:
# 1. Turn on Block Public Access for the bucket
aws s3api put-public-access-block --bucket secure-bucket \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
# 2. Disable ACLs so the bucket owner owns everything
aws s3api put-bucket-ownership-controls --bucket secure-bucket \
--ownership-controls 'Rules=[{ObjectOwnership=BucketOwnerEnforced}]'
# 3. Set default SSE-KMS encryption with a bucket key
aws s3api put-bucket-encryption --bucket secure-bucket \
--server-side-encryption-configuration \
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"arn:aws:kms:us-east-1:111122223333:key/abcd-1234"},"BucketKeyEnabled":true}]}'
# 4. Attach a bucket policy that denies non-TLS traffic (apply the JSON shown earlier)
aws s3api put-bucket-policy --bucket secure-bucket --policy file://deny-insecure-transport.json
Run through this until each step’s purpose is obvious. The exam won’t ask you to type commands, but it will ask which of four configurations achieves a stated outcome — and having built the outcome yourself makes the right option jump out.
Common Mistakes to Avoid
- Reading the bucket policy without checking Block Public Access. BPA overrides permissive policies; always check it first when a question asks whether an object is public.
- Granting S3 permissions but forgetting KMS. Reading an SSE-KMS object needs
kms:Decrypton the key — a bucket policy alone won’t do it cross-account. - Using
aws:SourceIpfor VPC endpoint traffic. Endpoint requests needaws:sourceVpce/aws:sourceVpc; source-IP conditions silently won’t match. - Assuming default encryption rejects weak uploads. It sets a default but doesn’t deny an override — add an explicit deny policy to enforce a specific key.
- Leaving ACLs enabled. Object-ownership surprises in cross-account writes are best solved by disabling ACLs, not by patching permissions after the fact.
Practice Until the Layers Are Automatic
S3 security rewards pattern recognition: once you can instantly see which layer a scenario is really about — public access, cross-account, encryption, network, or detection — the questions get much faster. The fastest way to build that reflex is repetition on realistic, explained questions.
Sailor.sh’s AWS Security Specialty mock exam bundle is modeled on the real SCS-C02 domain weights, so S3-heavy topics get the coverage they deserve — and every question ships with an explanation of why an answer is right, not just which option to pick. Benchmark yourself with the free SCS-C02 practice questions, structure your prep with the SCS-C02 study plan, and see how S3 connects to the rest of the blueprint in the AWS Security Specialty domains breakdown and the complete SCS-C02 exam guide for 2026.
Frequently Asked Questions
Does Block Public Access make my bucket fully secure?
No. It only prevents public exposure. A bucket with BPA enabled can still be accessed by trusted principals, other accounts you’ve granted, or over-broad IAM roles. Combine BPA with least-privilege policies, encryption, and network restrictions for defense in depth.
What’s the difference between a bucket policy and an IAM policy for S3?
An IAM policy attaches to a principal and travels with that user or role; a bucket policy attaches to the bucket and states rules about the resource itself. Same-account access needs an allow from either; cross-account access needs an allow from both the caller’s IAM policy and the bucket policy. Use bucket policies for resource-wide rules like TLS enforcement, encryption requirements, and VPC restrictions.
How do I let another AWS account read my KMS-encrypted objects?
Two grants are required. Allow s3:GetObject via the bucket policy (or a role), and allow kms:Decrypt on the KMS key via the key policy for the external principal. Missing the KMS permission is the most common cause of AccessDenied on cross-account reads of encrypted objects.
When should I use a presigned URL instead of making a bucket public?
Whenever you need to grant temporary, object-specific access to someone who isn’t authenticated to AWS — for example letting a user download one report or upload one file. The URL carries the signer’s permissions and expires, so the bucket stays private.
How do I ensure data only moves within my VPC?
Create an S3 gateway VPC endpoint so traffic stays on the AWS network, then add a bucket policy that denies requests where aws:sourceVpce isn’t your endpoint (or aws:sourceVpc isn’t your VPC). This is a core Infrastructure Security pattern on the exam.
Are ACLs still relevant for the SCS-C02 exam?
You need to recognize them, but the modern best practice — and usually the correct answer — is to disable ACLs with Object Ownership set to Bucket owner enforced, which makes the bucket owner own all objects and moves access control entirely to policies.
Ready to make S3 a strength rather than a liability? Drill realistic, explained questions with the Sailor.sh SCS-C02 mock exams, then connect the dots across domains with the AWS Security Specialty exam guide for 2026.