Prevent image hotlinking with S3 and CloudFront

1.3k Views Asked by At

I have a website with many pages where each page has many images (dozens or even hundreds).

I'm trying to avoid image hotlinking, but without increasing AWS costs too much.

So far I found 3 options:

Option 1: Use WAF to prevent hotlinking, by creating a rule to block based on referer header.

https://aws.amazon.com/pt/blogs/security/how-to-prevent-hotlinking-by-using-aws-waf-amazon-cloudfront-and-referer-checking/

The problem of this solution is that if you have many images loaded on each page, the cost will increase too much, bacause beyond paying S3 and CloudFront, you will need also to pay WAF, for every image request (imagine if you have 100 images on each page, for example).

Option 2: Configure CloudFront to fire a Lambda@Edge Viewer Request trigger that will inspect each request as it comes in the front door, and block requests based on referer header.

https://stackoverflow.com/a/46044606/2444386

This is similar to option above. The problem is that you add an overhead to EVERY request, even if the image is already in CloudFront cache. The Lambda@Edge will always be called and will also increase too much the cost if you have many images on each page of your website.

Option 3: Configure S3 bucket policy to block requests based on referer header, and whitelist the referer header at CloudFront.

So far, this seems to me the option with the best cost benefit, because if the image is already in CloudFront cache, you don't have any overhead, and also the cost is the cheapest because you don't need to pay WAF nor Lambda@Edge.

The problem is that the cache hit ratio will be much smaller, because CloudFront will not serve a response from cache unless the incoming request's Referer header matches exactly one from an already-cached request.

I tried to use the "origin" request header to avoid this problem, but it seems browser don't send this header for image GET requests.

Is there a better option?

2

There are 2 best solutions below

0
Daniel Barral On BEST ANSWER

I ended up using option 3 (S3 bucket policy) and additionally using the free plan of CloudFlare. CloudFlare has "Hotlink Protection" in the "Scrape Shield" even in the free plan.

S3 bucket policy to prevent hotlinking:

{
    "Version": "2012-10-17",
    "Id": "http referer policy",
    "Statement": [
        {
            "Sid": "Allow access only from my website",
            "Effect": "Deny",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::resources.mywebsite.com/*",
            "Condition": {
                "StringNotLike": {
                    "aws:Referer": [
                        "https://dev.mywebsite.com:8080/*",
                        "https://staging.mywebsite.com/*",
                        "https://www.mywebsite.com/*"
                    ]
                }
            }
        }
    ]
}

In CloudFront I whitelisted the Referer header:

enter image description here

And in CloudFlare you can enable the "Hotlink Protection" option in the "Scrape Shield" menu:

enter image description here

enter image description here

0
Chris Williams On

You could avoid these above by using a native option supported by CloudFront, Signed Cookies.

By adding a Lambda@Edge function if the user lands on a non asset based page (i.e. the home page) you could generate a signed cookie. By enforcing this in CloudFront only people who are browsing your website will be able to access the assets.

Alternatively you can generate a CloudFront signed URL, this would need to be done for each asset though.

To use these create a secondary origin / behaviour for the assets you want protected and add the behavioural rule of Restrict Viewer Access (Use Signed URLs or Signed Cookies). The Lambda@Edge would live in the other origin.