13

I'd like to allow anyone to play a video located in my s3 on my site as the src on a <video> tag but not allow people to use it as a src on their site or to play the video directly by typing the url into the browser bar.

I don't want people doing this:

enter image description here

and I don't want the following HTML to appear on http://your-site.com but only on http://my-site.com:

<html>
    <video src="https://s3.amazonaws.com/my-bucket/my-video.mp4"></video>
</html>

I've seen some SO links on this but I wanted to talk in code since I haven't been able to make these solutions work for me.

Here's my bucket policy that is currently NOT working:

{
"Version": "2008-10-17",
"Statement": [
    {
        "Sid": "AllowPublicRead",
        "Effect": "Allow",
        "Principal": {
            "AWS": "*"
        },
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::my-bucket/*",
        "Condition": {
            "StringLike": {
                "aws:Referer": [
                    "https://my-site.com/*"
                ]
            }
        }
    }
  }

Two questions:

  1. To test my bucket policy, I put the above HTML in a test file on my localhost and sure enough I can access the video by typing http://localhost/test.html. Why isn't my bucket policy preventing this? (I'd only want it to work from http://my-site.com/test.html)
  2. To prevent people from inputing the s3 URL into the browser bar, I was thinking I need a separate solution from the bucket policy since it's not clear to me from the AWS documentation how to prevent direct access via the browser. I was thinking of hashing the url to make it hard to guess. Perhaps there are ways using the AWS bucket policy or other solutions though?

To be more clear, my files are stored on s3 but they are delivered by Amazon's CloudFront. So my CloudFront url src is currently media.my-site.com/my-video.mp4. The CNAME being media.my-site.com.

Community
  • 1
  • 1
tim peterson
  • 23,653
  • 59
  • 177
  • 299
  • 4
    _"I was thinking of hashing the url to make it hard to guess."_ — Security by obscurity is a bad plan. – HellaMad Jul 17 '12 at 15:12
  • @DC_, thanks ok I won't do that. Might you have suggestions as for what I should do? – tim peterson Jul 17 '12 at 15:14
  • @tim Unfortunately I have little to no experience with AWS, so I can't help you with the bucket policy thing. I think I have an idea though and I'll post it as an answer. – HellaMad Jul 17 '12 at 15:17
  • @timpeterson, Sorry, I thought you were talking about CloudFront -> S3. My (deleted) comment doesn't apply. – Matthew Jul 17 '12 at 15:20
  • @Matthew my files are stored on s3 but they are delivered by CloudFront. So my `src` CloudFront url is currently something like this `media.my-site.com/my-video.mp4`. Does that help explain better? I'll make this more clear in my question. – tim peterson Jul 17 '12 at 15:21
  • @timpeterson, then yes, I believe my comment was valid. ;) From the limited research I recently did, S3 sees CloudFront as the referrer, not the browser. This is unfortunate, so I think the only thing you can do is mix up your CNAMEs on a regular basis, and ask Amazon to add better support for this. – Matthew Jul 17 '12 at 15:25
  • @Matthew ok thanks, yes currently `media.my-site.com` is my CloudFront CNAME but I guess I can break offender's links but not my own by changing this CNAME regularly. I'll look into contacting AWS on this. – tim peterson Jul 17 '12 at 15:27

2 Answers2

6

Given that CloudFront currently does not let you directly restrict access (to the best of my understanding), I would do something like:

<video src="/media.php?v=my-video.mp4"></video>

Then your media.php file looks like:

if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] != 'my-site.com')
{
  header('HTTP/1.1 503 Hot Linking Not Permitted');
  // display some message / image / video
  exit;
}

# this base url changes from time to time
$url = 'http://cdn.my-site.com';

header("Location: $url/{$_GET['v']}");

To make it less obvious, you may want to set up a rewrite to route /media/my-video.mp4 into the file. That way, it doesn't look like there is an intermediate PHP script.

Exactly how you do the referrer check depends on the level of security you want. Some people disable referrers, so you may want to allow empty ones. Or you could even check to see if a session variable or cookie exists, etc.

Of course, the end user will be able to sniff out the real URL. This is why you may want to change your CNAME from time to time.

This solution is hopefully good enough to discourage people from abusing your site, but is by no means perfect.

Matthew
  • 47,584
  • 11
  • 86
  • 98
  • hi @Michael, thanks for your answer. So by "sniff out the real URL", you mean get it from the source code/Firebug/Developer Tools? It seems like the rather than try and figure out the logic of the php file, the easy route for theives is: 1) get URL (the CloudFront one) from my-site.com source code (Assuming they get accessly legally a first time). 2) put URL in your-site.com source code 3) hope my-site.com doesn't change their links (i.e., CNAME). Are we on the same page? – tim peterson Jul 17 '12 at 17:54
  • 1
    Yes, that is correct. Using something like Firebug, they could look at HTTP traffic and see that the movie is just a redirect to CloudFront. That URL would not be protected, hence you update your CNAME from time to time. – Matthew Jul 17 '12 at 18:04
4

Instead of directly linking to your S3 files, can you use PHP as a proxy so that the end user never sees the actual S3 URL, and you can verify the referer more easily? You would probably need some sort of database for this though, so you can link an ID to an S3 file. For example (disregarding security measures):

<?php
$file = $_GET['id'];
$referer = $_SERVER['HTTP_REFERER'];

if($referer === 'http://my-site.com/test.html'){
    $s3 = //Query your database of S3 files with the ID provided to get the S3 URL
    header(mime_content_type($s3));
    include($s3);
}
?>

This can be saved as get_file.php or whatever you want, and you can just put links like http://my-site.com/file/get_file.php?id=120381 in your HTML instead.

To go a step further, you can use a .htaccess file to route requests like http://my-site.com/file/120381.mp4 to http://my-site.com/file/get_file.php?id=120381.

I'm not sure how well this would work, and the PHP code I provided was just an example to help convey my idea; it is not perfect code so please don't downvote me just for that.

HellaMad
  • 5,294
  • 6
  • 31
  • 53
  • 3
    `HTTP_REFERER` can be set by the client. – Luc M Jul 17 '12 at 15:37
  • 1
    @LucM Yeah, good point. I still think this method is more secure than the obscurity method; if anyone has a better idea please make a new answer! – HellaMad Jul 17 '12 at 15:39
  • @LucM Thanks for the solution that looks like it will work for hotlinking. But don't you agree "security by obscurity" is the only option if someone is trying to get your file by typing stuff into the browser bar? – tim peterson Jul 17 '12 at 15:43
  • @tim peterson If you use this solution, your mp4 files do not need to be in the web-root so there will be no public url available to get to them or you can use an `.htaccess` file to deny access from all. – jeroen Jul 17 '12 at 15:56
  • @jeroen i'm kinda confused, are you saying I need to do this: ` – tim peterson Jul 17 '12 at 16:21
  • @tim peterson Yes, and check for the referrer so that only your site can include them. Not 100% proof, but it will probably prevent all hotlinking and direct loading. – jeroen Jul 17 '12 at 16:25
  • @timpeterson Using this solution, the end user **should never even see** the AWS url. – HellaMad Jul 17 '12 at 16:27
  • @DC_ oh yeah, whoops on the PHP tags, pretty dumb! ok, what will a developer see if they `inspect element`? At the end of the day, the url has to be a URL with .mp4 at the end which can be seen from developer tools/Firebug. Maybe, I'm misunderstanding? – tim peterson Jul 17 '12 at 16:35
  • 1
    Yes, `HTTP_REFERER` can be set by the client, but the majority of people use valid referrers, which makes the check an effective way to discourage hot linking embedded content. The bigger issue here is that while the URL will not be seen directly, it can still easily be discovered by using any browser dev tools by sniffing HTTP traffic. So I would combine this with CNAMEs that you update on a regular basis. – Matthew Jul 17 '12 at 16:35
  • 2
    Regarding the answer itself, you do not want to "include" the CloudFront content. You want to redirect to it. Including the file and passing it along would defeat the entire purpose of CloudFront. The point is, that people will naively copy/paste your PHP link, which has the HTTP referrer check. If they get at the real CF link, that will only work for a short time until you update the CNAME. Not fool proof, but probably effective unless you have content that people *really* want to steal. – Matthew Jul 17 '12 at 16:37
  • @Matthew so in conclusion, you say just use the cloudFront link like this: `` and update it periodically to something else like this ``. I realize nothing is perfect but seems worth it to catch lazy criminals. – tim peterson Jul 17 '12 at 16:44
  • @timpeterson, I've added my own answer since it deviates from this one and it's too hard to properly explain in these comments. – Matthew Jul 17 '12 at 17:41