Serving Private Content from CDN

Written by lakindu | Published 2019/08/09
Tech Story Tags: cdn | privatecontent | signedurl | signedcookies | aws | latest-tech-stories | serving-private-content | programming

TLDR Amazon Web Services is a cloud service platform which offers content delivery, database storage and many other functionalities and infrastructure on demand. Amazon CDN is a main component of AWS in order to speed up the distribution of your static or dynamic web content to the users around the globe. Every website does not share every content with the user. There are websites for paid users or maybe some private content shared among the subscribed users. The solution is to use Signed Urls and Signed Cookies to access private content.via the TL;DR App

Amazon Web Services is a cloud service platform which offers content delivery, database storage and many other functionalities and infrastructure on demand in order to help businesses to scale and grow with “pay as you go” pricing method.
Amazon CDN is a main component of AWS in order to speed up the distribution of your static or dynamic web content to the users around the globe.
Ex: Let’s just say you have hosted your website in USA region. And if some user from Australia trying to access your website, the routing usually goes to USA and receive the content to your user. What if your user again access the content, the process takes abit time to deliver your content which means high latency.
For this issue, amazon CDN provide will cache your website in the nearest edge location and deliver the content to your users within Australia using that edge location. Before it sends, it will sync up with the original content and send you the latest details. With this it will take a low latency.
But every website does not share every content with the user. There are websites for paid users or maybe some private content shared among the subscribed users. And there are companies which distribute content via internet which has restrict access such as business data, media streams.
How are we sharing the private content only to the specified users among thousands of users?
For an example, if your application requires a subscription, which means only private content in your website is shared among subscribed members, can be achieved from Amazon CDN’s private content feature. It will ensure that only authenticated users can access and prevent other users accessing your private content.

Solution

Using Signed Urls and Signed Cookies to access private content.

Signed Urls

Signed Urls is a url which contains the information such as expiration date, key pair id and signature for each individual file in the private content. The mentioned data will be included in the url which we are sending.
How Signed Urls work,
Specify the trusted signers in the CDN distribution whom can access the content.After developing the application, in order to access your content, it will create signed URLs for the objects or parts of the application which is restricted.When a user requests for file, then application verify the user.After verifying, application creates and return signed url to the user.Then restricted data can be accessed to the specified user.CDN uses public key to validate the signature and confirm the Url is valid. If invalid signature, request will be rejected.

Signed Cookies

CDN signed cookies allows you to control your private data and distribute them among the users you want to share with. And signed cookies can be used to give access to multiple restricted files without specifying URLs.
How Signed Cookies work,
In the CDN distribution we specify the trusted signers in order to them to access.After developing the application, in order to access your content, we have to send three set-cookie headers to the viewer(user). They are CloudFront-Key-pair-id, CloudFront-Signature and CloudFront-Policy. We must send the set-cookie headers to the user before the viewer requests.A user will access to your private content (subscriber).
Our application returns set-cookie headers to the user in the response.Then the user requests an object.In the request headers, CDN gets the signature in the signed cookie and then validate it with the public key which was created in the root account earlier. After validating with the key, if the request is valid, it will check for the policy, which checks the expiry dates and other options. Then it will access users requested content.

To start off, there are few prerequisites

AWS AccountStatic Web hosted in S3 Bucket or Custom Origin which hosts a lambda function (API Gateway)
Note that there will be zero cost involved if you use a free tier account from AWS. If not, you can refer the pricing of CDN in the link given and no other cost involved performing these steps. https://aws.amazon.com/cloudfront/pricing/
Step 1: Create a CloudFront Key Pair
Login from your ROOT Account
Go to top right side User tab > Select ‘My Security Credentials’ > ‘CloudFront Key Pairs’ > ‘Create a new key pair’.
Download the key pairs after creating the key pair. And you can find the ‘Key Pair Id’ as Access Key ID on the screen. And also in the filename which you downloaded.
NOTE: There can be only one key pair in your root account, make sure you don’t have several active key pairs created in the same root account.
Step 2: Create a CDN distribution
Make sure in Object Caching, choose Customize and enter 0 to all values then browser will avoid caching any cookie values since you will be giving different cookie values each time you access, therefore even though caching is the main reason for a CDN, at this point it will be not useful. And select “Yes” for the restrict viewer access because it is the purpose of using Signed Cookies/URLs, to enable secure content distribution. For testing purposes, All cookies and query string forwarding and caching is enabled.
Now after creating the content private in the CDN, we have two ways of accessing the content.
Signed URL Signed Cookies
Signed URL and Cookies provide the same basic functionality which allows you to control your private content.
Canned Policy and Custom Policy
Canned Policy is a pre-written policy which is less controllable compared to custom policy. The matrix below was provided in AWS Documentation.
Step 3.1 — Using Signed URL
You can use the following code in a lambda function with the required modules in order to get the signed URL to your content.

1. Include ‘aws-cloudfront-sign’ module.
2. Enter your private key or the pk-APXXXXXXXXXXXXX.pem file
3. In the option, define Key-pair-id, expire time
4. Enter cloudfront url with the specific content you want to restrict
var cf = require('aws-cloudfront-sign');
var privateKeyString = ` - - -BEGIN RSA PRIVATE KEY - - -
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 - - -END RSA PRIVATE KEY - - -`;
var options = {keypairId: 'APXXXXXXXXXXXXXXXXX', privateKeyString : privateKeyString, expireTime: new Date().getTime() + 18000}
exports.handler = (event, context, callback) => {
       const done = (err, res) => callback(null, {
         statusCode: err ? '400' : '200',
         body: err ? err.message : res,
         headers: {
          'Content-Type': 'application/json',
         },
      });
var signedUrl = cf.getSignedUrl('http://xxxxxxxx.cloudfront.net/path/to/object', options);
context.done(null, signedUrl);
};
Step 3.2 — Signed Cookies
Include ‘aws-cloudfront-sign’ moduleEnter your private key or include the pk-APXXXXXXXXXXXXX.pem fileIn the option, define Key-pair-id, expire timeEnter cloudfront Url as wildcard.
var cf = require('aws-cloudfront-sign');
var privateKeyString = ` - - -BEGIN RSA PRIVATE KEY - - -
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 - - -END RSA PRIVATE KEY - - -`;
var options = {keypairId: 'APXXXXXXXXXXXXXXXXX', privateKeyString : privateKeyString, expireTime: new Date().getTime() + 18000}
exports.handler = (event, context, callback) => {
   const done = (err, res) => callback(null, {
     statusCode: err ? '400' : '200',
     body: err ? err.message : res,
     headers: {
       'Content-Type': 'application/json',
     },
   });
var signedCookies = cf.getSignedCookies('http://xxxxxxxx.cloudfront.net/*, options);
context.done(null, signedCookies);
};
Refer documentation for further changes such as changing to custom policy
If you are getting an error, module not found when testing the lambda function, include the node module.

1. Install Node in your computer
2. Create a folder and inside the folder create a package.json file
3. Create an index.js file and input above signed url or signed cookie code in the file
4. Install the aws-cloudfront-sign module
5. Zip the project and upload to lambda function under Code entry type > Upload a zip file
Conclusion
Hope this will help you to create private content and serve it with Signed Cookies and Signed Urls. For signed cookies, there were no proper tutorials to help out with examples. If you have any issues, you can comment below.

Written by lakindu | Software Engineer
Published by HackerNoon on 2019/08/09