AWS CloudFront Signed Cookie Laravel Middleware
When a issue comes across and it takes me 6+ hours to figure out the problem by changing values one at a time, those are the real programming challenges. They are frustrating at the time, but once they are solved it is a very rewarding experience.
I wanted to use AWS CloudFront CDN to cache images and audio files for an application I was building. The problem is if someone figured out the file name path they would be able to data mine all the files in the S3 bucket. There are two methods (that I know of) that you can use to protect your assets. The first is using Signed URLs which are prepended to the media url path, this method works well but you would need to write a hook to pre-sign every CDN asset before sending it to the page to render. The other method is a CloudFront Signed Cookies. I chose to go with this method for my project to serve the protected assets.
The major problem with trouble-shooting issues with CloudFront are the error messages are extremely vague. You can turn on CloudFront logging to a S3 bucket but the error messages are of no hope. I spent many errors changing single variables at a time to figure out the issue.
For all your sake I’m going to document the technique I used to write a Laravel middleware to create the CloudFront Signed Cookies for my app.
For the sake of this tutorial we are going to assume you already have a S3 bucket setup with your assets. Also you will need a domain name that is hosted live on the internet.
Create a new public private keys
In your terminal create a new private key with:
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public.key.pemto extract the public key from the
More info can be found here:
Create a new CloudFront Distribution
Make sure you CDN is the same domain as the site you want to retrieve assets from. If your domain name is
example.com your CloudFront domain should be something like
cdn.example.com . The reason for this is you are going to be creating cookies that work on a domain that you
own. You won’t be able to create a cookie for another site.
Create a new CloudFront distribution.
Under “Origin domain” select the S3 bucket with your assets.
Under the “Default cache behavior” panel select “YES” under “Restrict viewer access” and add the key group that you created.
In the “Settings” panel add a custom SSL certificate with your domain eg:
*.example.com Under “Alternate domain name (CNAME)” create a new item with the domain you want for your CloudFront distribution like:
Create your CloudFront distribution, allow CloudFront to update the S3 bucket policy, this should take a bit about 10 minutes or so.
Add a new Route 53 Record
Under your domains hosted zone add new a record for your CloudFront distribution. Set the record name to be the same name you set your CloudFront CNAME.
Beneath the record name change the Alias toggle, you should see the CloudFront distribution in the drop down list.
If you don’t see your distribution in the drop down you can use record type “CNAME” and for the value use the CloudFront Domain name which should look like:
abc14ddad499.cloudfront.net . Create the record in the Route53 hosted zone.
Update the S3 Bucket CORS Policy
Select the S3 bucket with your assets and select “Permissions” tab. Towards the bottom in the CORS Policy panel update the policy to match below. Change the
example.com to your domain and your CDN domain. (Note: the CDN domains may note be needed, but this solution worked for me).
In your laravel application install the
composer require aws/aws-sdk-php
private_key.pem private key file you created to the
.env file add the following:
# Found under CloudFront -> Public Keys
CLOUDFRONT_KEY_ID="Cloudfront Public Key ID"# The domain name of your cloudfront distribution
config/services.php add the following array values:
'cloudfront' => [
'key_id' => env('CLOUDFRONT_KEY_ID'),
'resource_key' => env('CLOUDFRONT_RESOURCE_KEY'),
Create the CloudFront Cookie Middleware
Using artisan create a new middleware:
php artisan make:middleware CloudFrontCookie
app/Http/Middleware/CloudFrontCookie.php file to look like:
Finally let’s add the CloudFrontCookie middleware to
app/Http/Kernel.php under the middleware group
UPDATE — Ignore CloudFront Cookies from being encrypted
app/Http/Middleware/EncryptCookies.php make sure to ignore the CloudFront cookies from being encrypted.
Update Note — Just went through this process again and couldn’t figure out why the CloudFront cookies weren’t working. They needed to be ignored from laravels default cookie encryption.
Test it out!
Deploy your application to your production domain and give it a whirl.
If all the moving pieces are put together correctly when you open you application (in production only), in the console storage cookies tab you should see 3 new cookie files that will authenticate your requests to access your protected S3 resources on CloudFront!
I spent a ton of time banging my head and making little tweaks and googling to figure all this out. Hopefully this article will be some help if you are having trouble or would like to use CloudFront signed cookies in your application. Let me know if you see any errors or would like anything exampled more clearly. Happy Coding!