Tutorial: Securing private content on AWS Cloudfront

Using a CDN such as Cloudfront is great for getting your static content closer to your users and offloading your server. It’s also very easy to set up. However, when you want to restrict content to logged in users, things get a bit more complicated. Especially when using your own webserver to serve content (custom origin). Recently we had to figure out how to do this with AWS Cloudfront. Their documentation is a good resource but can be a bit overwhelming. In this short tutorial we will focus on how to achieve it using PHP.

Prerequisites

  • An AWS account
  • Some static content to secure, for example images. These should be publicly available on the web. We will cover securing the communication channel between Cloudfront and your webserver by the end of the tutorial.

S3 vs custom origin

Cloudfront gets the original resources from what’s called an origin. This can be either S3 or a custom origin that can be any domain name. The procedure for securing S3 content is different and is described here. We will focus here on a custom origin.

Signed URLs vs signed cookies

These are the two main methods. We have a requirement that we secure every file individually so even logged in users cannot easily scrape all the content. As the signed cookie method is intended for securing multiple files with a single cookie we will go for the signed URL method.

Canned policy vs custom policy

These methods are very similar. The difference is that a canned policy can be constructed by Cloudfront based only on the URL parameters. So you don’t have to send it over explicitly and this saves a bit on the URL length. Canned policies are less powerful but are sufficient for our use case so we will go for a canned policy.

STEP 1: create Cloudfront key pair

You need to login to your AWS account using root credentials. You cannot do this via an IAM user at the moment.

Location of “My Security Credentials”

Go to “My Security Credentials > Cloudfront Key Pairs” and create your key pair. Make sure you download the private key after creation and note the key ID (which is also in the filename of the downloaded key).

STEP 2: create Cloudfront distribution

Go to the Cloudfront Distributions page and create a new distribution. You can get more info on all options through the documentation but we will highlight a few points:

Create Cloudfront distribution
  1. Origin Domain Name - this is the location of the original files
  2. Origin Protocol Policy - make sure this is https only to minimise the risk of exposing the custom header
  3. Origin Custom Headers - with this header you can prevent bypassing Cloudfront, see step 4 from this tutorial
The header used is just an example, but you can use any header. For example you could utilise an existing OAuth mechanism.

The last step is actually restricting the access to the assets. We assume here that your Cloudfront key is created in the same account as where you create the distribution.

Restrict access

STEP 3: sign URL’s

You can use the following code to create signed URL’s on your server.

  1. Update the $keyPairId to the ID of the key created in step 1
  2. Make sure your private key is present on the server and change the code to point to the key. Do not place the key in your webroot!
  3. Update the example URL at the end with your distribution URL

STEP 4: securing the origin

To prevent users from bypassing Cloudfront and request the content directly you will have to secure the channel. In step 2 you created a custom header that will be sent by Cloudfront. Make sure that your webserver only allows requests with that header present on your origin server.

Note that you will need to make sure requests from Cloudfront are https only (with the Origin Protocol Policy) to prevent your secret header from being exposed.

Conclusion

I hope this post saves you some time. For more customization options refer to the AWS documentation.

Please let me know your experiences in the comments!