Back to Blog
Lesson 13 of the AWS: AWS Core Services for Developers course
Cloud NativeJuly 13, 20264 min read

Managing S3 Bucket Policies: A Secure Access Control Guide

Master S3 Security: Learn to apply robust bucket policies, disable public access, and generate pre-signed URLs for secure, private object storage.

AWSS3SecurityCloudFormationInfrastructure as CodeIAM

Previously in this course, we covered the fundamentals of object storage in Introduction to Amazon S3: Object Storage for Developers. While that lesson focused on creating buckets and basic uploads, today we focus on S3 Security, specifically how to manage access control so your data remains private while still being accessible to authorized users.

Understanding S3 Security Principles

In AWS, security is a layered approach. By default, every S3 bucket is private. When you need to share data, you have three primary tools:

  1. Block Public Access (BPA): A "master switch" that overrides all other permissions to ensure no object is publicly accessible.
  2. Bucket Policies: JSON-based resource policies that define who can access the bucket and what they can do.
  3. Pre-signed URLs: Temporary tokens that allow time-limited access to private objects without requiring the user to have AWS credentials.

Disabling Public Access

Before writing any policies, you must ensure your bucket is "locked down." AWS provides a feature called S3 Block Public Access. Always enable this at the account or bucket level unless you are explicitly hosting a public website.

Using the AWS CLI, you can enforce this:

Bash
aws s3api put-public-access-block \
    --bucket your-unique-bucket-name \
    --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

Setting these to true acts as a safety net, preventing accidental exposure even if a misconfigured policy is applied later.

Applying a Bucket Policy

A bucket policy is a JSON document that uses the IAM policy language. It consists of Effect (Allow/Deny), Principal (who), Action (the S3 operation), and Resource (the bucket/objects).

If you want to grant a specific IAM user read-only access to your bucket, your policy would look like this:

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:user/your-app-user"
      },
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::your-unique-bucket-name/*"
    }
  ]
}

To apply this, save it as policy.json and run: aws s3api put-bucket-policy --bucket your-unique-bucket-name --policy file://policy.json

Generating Pre-signed URLs for Private Access

In a real-world application, you often need to share a private file (like a user profile image or a generated report) for a very short duration. Instead of changing bucket permissions, we generate a pre-signed URL.

This URL includes a signature in the query string that validates the request. Here is a simple Node.js example using the AWS SDK v3:

JAVASCRIPT
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const s3 = new S3Client({ region: "us-east-1" });

async function getPrivateFile(bucket, key) {
  const command = new GetObjectCommand({ Bucket: bucket, Key: key });
  // URL expires in 3600 seconds(1 hour)
  const signedUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
  console.log("Access this file at:", signedUrl);
}

Hands-on Exercise

  1. Lock it down: Run the put-public-access-block command provided above on your project bucket.
  2. Restrict access: Create a policy that allows only your specific IAM user to access the bucket.
  3. Generate a link: Use the Node.js snippet above to generate a URL for a file in your bucket. Try opening it in an Incognito window—it should work! Wait an hour, and it should return an AccessDenied error.

Common Pitfalls

  • The "Deny" Trap: If you have an explicit Deny in any policy, it overrides any Allow. Check your IAM user policies and the bucket policy if access is failing.
  • Missing /*: When defining resources in a policy, remember that arn:aws:s3:::my-bucket refers to the bucket itself, while arn:aws:s3:::my-bucket/* refers to the objects inside. Forgetting the /* is the most common reason for "Access Denied" on object downloads.
  • Credential Scope: Pre-signed URLs are signed using the credentials of the user who generated them. If that user's permissions change, the URL will fail.

FAQ

Q: Why use pre-signed URLs instead of making files public? A: Pre-signed URLs allow you to keep your bucket private and secure, ensuring that only authenticated users can access the data, and only for a limited window of time.

Q: Can I use wildcards in the Principal field? A: You can use * for the Principal in public-facing policies, but this is dangerous. Always prefer explicit ARNs for internal applications.

Recap

We've successfully moved from open storage to a secure model. By applying S3 Security via Bucket Policies and leveraging Access Control patterns like pre-signed URLs, we ensure that our serverless web app keeps user data private while still providing functional access where needed.

Up next: We will dive into DynamoDB Data Modeling for Developers, where we'll design the schema to store the metadata for the files we are managing in S3.

Similar Posts