5

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?

Daniel Barral
  • 3,896
  • 2
  • 35
  • 47
  • What solution did you go with in the end? I am in the same position. What's frustrating is that I had this solved when I was just using S3 without CloudFront; I had a policy on my S3 bucket that rejected all requests that didn't originate from my website. However, with CloudFront I can see no way to apply policies like this. I can restrict my S3 to only allow traffic from CloudFront, but I cannot see how to apply a similar policy to CloudFront to restrict access only to requests coming from my site. What did you end up doing? – MSOACC Dec 10 '20 at 00:39
  • Hi @MSOACC , I posted an answer with the details of what I ended up doing. I reccomend you using CloudFlare if you can. They have an option of hotlink protection. Additionally the lowered my costs with CloudFront. – Daniel Barral Dec 10 '20 at 14:23
  • Thanks, didn't see it. You should mark it as the accepted answer. – MSOACC Dec 10 '20 at 14:25
  • The only disadvantage of CloudFlare is that you will need to change your DNS entries. But if you can, I would recommend, because you also will gain DDoS protection. – Daniel Barral Dec 10 '20 at 14:28
  • 1
    I just posted the answer now, after you asked me. I will mark as accepted answer. – Daniel Barral Dec 10 '20 at 14:29

2 Answers2

1

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.

Chris Williams
  • 32,215
  • 4
  • 30
  • 68
1

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

Daniel Barral
  • 3,896
  • 2
  • 35
  • 47