How to Restrict S3 Access Using CloudFront Signed Cookies and URLs

In this post, we will understand how to use AWS CloudFront restricted access settings (signedCookies and signedURLs) to secure access to S3 objects.

When we use S3 with CloudFront (and OAC), by default, all objects are publicly accessible over the internet. There is not control on who can access which object.

Imagine a scenario, where you want files to be accessible only by a person who uploaded them. Or maybe you want only authenticated users to access files. Or maybe users with certain permissions (groups) only should be able to access files. CloudFront restricted access is useful in such scenarios.

How Does CloudFront Restricted Access Work?

It involves three steps:-

  1. Create a public-private RSA key pair.
  2. Configure public key with CloudFront distribution.
  3. Use private key to create Signed URL or Cookies

Before requesting files, the client (react app) calls the backend which uses the private key to create a signature. Then request is made to CDN and CloudFront uses the public key to verify signature validity. If the signature is valid, access to the object is granted!

How To Create CloudFront Signed Cookie?

AWS CloudFront-Signer SDK can be used to create signedCookie or signedURLs. And lambda function can be used as a backend as a cookie vending machine 🍪

import { getSignedCookies } from "@aws-sdk/cloudfront-signer";

const privateKeyRaw = process.env.privateKey;
const privateKey = privateKeyRaw.replace(/\\n/g, '\n');

const cloudfrontDistributionDomain = process.env.cloudfrontDistributionDomain;
const keyPairId = process.env.keyPairId;
const intervalToAddInMilliseconds = 86400 * 1000; // 24 hours in milliseconds 


export const handler = async (event) => {

  if (!privateKey) {
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'privateKey not set in environment variables' }),
    };
  }

  // const userSub = event?.requestContext?.authorizer?.jwt?.claims?.sub;
  // const s3ObjectKey = `images/${userSub}/*`;

  const s3ObjectKey = "images/*";
  const url = `${cloudfrontDistributionDomain}/${s3ObjectKey}`;
  const dateLessThan = Math.floor((Date.now() + intervalToAddInMilliseconds) / 1000);


  const policy = {
    Statement: [
      {
        "Resource": url,
        Condition: {
          DateLessThan: {
            "AWS:EpochTime": dateLessThan,
          },
        },
      },
    ],
  };
  const policyString = JSON.stringify(policy);


  const cookies = getSignedCookies({
    keyPairId,
    privateKey,
    policy: policyString,
  });

  const expires = new Date(dateLessThan * 1000).toUTCString();

  return {
    statusCode: 200,
    headers: { "Content-Type": "application/json" },
    cookies: [
      `CloudFront-Key-Pair-Id=${cookies['CloudFront-Key-Pair-Id']}; Expires=${expires}; Path=/; Secure; HttpOnly; SameSite=None;`,
      `CloudFront-Signature=${cookies['CloudFront-Signature']}; Expires=${expires}; Path=/; Secure; HttpOnly; SameSite=None`,
      `CloudFront-Policy=${cookies['CloudFront-Policy']}; Expires=${expires}; Path=/; Secure; HttpOnly; SameSite=None;`,
    ],
    body: JSON.stringify(cookies),
  };

};

Signed Cookies To Secure Images

The above architecture can be used to secure access to images stored in the S3 bucket. Lambda will create cookies for “/images/cognitoUserId/*”. Checkout full code on GitHub:- https://github.com/TrickSumo/AWScode-2025/tree/main/SignedCookies

Signed Cookies Vs Signed URLs

Signed URL is suitable to grant access to a single object. For example, to grant access to eBook pdf purchased by any user. Whereas, Signed Cookies are used to grant access to many files. For example, access to all images in some folder.

CloudFront Signed URLs vs S3 Pre-Signed URLs

FeatureCloudFront Signed URLS3 Pre-Signed URL
Used ForSecure access to CloudFront-distributed content (CDN URL)Secure access to S3 objects directly (bucket.s3.amazonaws.com)
Underlying AuthRSA Key Pair (Public/Private)IAM Credentials (Access Key + Secret Key)
Key Usage– Public key configured in CloudFront
– Private key used to sign URL or cookie
– IAM credentials used to sign URL (SigV4)
PurposeGet itemGet or Put object

Thanks 🙂

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.