S3 Image Security with CloudFront and Lambda@Edge

TL;DR: A pattern for serving private S3 images through CloudFront using Lambda@Edge for URL signing with time-limited validity.

Overview

When serving user-uploaded images from S3, public bucket access creates security risks. This document outlines a pattern using CloudFront with Lambda@Edge to serve private S3 content with time-limited signed URLs.

Architecture Components

ComponentPurpose
S3 BucketPrivate storage for images
CloudFrontCDN distribution with origin access
Lambda@EdgeURL signing and hash resolution
DynamoDBHash-to-URL mapping with TTL

Security Implementation

The current security model for protected images:

  1. Private S3 Bucket: Images stored in S3 with blocked public access
  2. CloudFront Distribution: Serves as the only entry point for image requests
  3. 30-Day URL Validity: Signed URLs have configurable expiration
    • For each S3 URL, a unique hash is generated and stored in DynamoDB
    • When a request arrives at CloudFront, Lambda@Edge resolves the hash to the actual S3 URL
    • After expiry, a new hash is generated on the next request

Request Flow

Client Request → CloudFront → Lambda@Edge

                            DynamoDB Lookup

                         (hash → S3 URL mapping)

                           S3 Private Bucket

Implementation Notes

  • Hash uniqueness ensures each S3 URL has exactly one active mapping
  • DynamoDB TTL automatically cleans expired mappings
  • Lambda@Edge runs at CloudFront edge locations for low latency
  • Origin Access Identity (OAI) restricts S3 access to CloudFront only

Benefits

  1. Zero public exposure: S3 bucket never directly accessible
  2. Time-limited access: URLs automatically expire
  3. Audit trail: Hash mappings provide request logging
  4. Edge caching: CloudFront caches validated responses