How to securely play .m3u8 streaming file from AWS S3 using CloudFront?
Asked Answered
T

2

6

I'm using CloudFront Signed URL to display images and videos from S3 to be secured.

It works well on images and other videos except for .m3u8 file.

I used AWS PHP SDK and here's my code.

<?php
    // Instantiate the CloudFront client.
    $cloudFrontClient = new CloudFrontClient(array(
        'region'        => env('AWS_DEFAULT_REGION'),
        'version'       => 'latest',
        'http'          => [ 'verify' => false ],
        'credentials'   => array(
            'key'           => env('AWS_ACCESS_KEY_ID'),
            'secret'        => env('AWS_SECRET_ACCESS_KEY'),
        )
    ));
    
    // Create a signed URL for the resource.
    $resourceKey = 'https://abcdefg.cloudfront.net/test/file_1000k.m3u8';

    $expires =  time() + 3600;
    $signedUrl = $cloudFrontClient->getSignedUrl([
        'url'         => $resourceKey,
        'expires'     => $expires,
        'private_key' => public_path() . '/pk-ABCD123.pem',
        'key_pair_id' => 'ABCD123ABCD123ABCD123'
    ]);
?>

<video id="hls-example" class="video-js vjs-default-skin" width="640" height="480" controls>
    <source src="<?php echo $signedUrl; ?>" type="application/x-mpegURL">
    Your browser does not support the video tag.
</video>
<script src="https://vjs.zencdn.net/7.2.3/video.js"></script>
<script src="<?php echo asset('public/assets/js/videojs-contrib-hls.min.js'); ?>"></script>
<script>
    var player = videojs('hls-example');
    player.play();
</script>

If I'm not mistaken, it doesn't play because we also need to sign the segmented files (.ts) inside the .m3u8 file.

How do I dynamically change it?

Is there any way we can play .m3u8 file securely so that users can't use the direct link access to download the file?

Tradescantia answered 28/5, 2020 at 1:53 Comment(0)
C
1

This is a good AWS article to help you decide between (1) cookies and (2) signed URLs: -

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-choosing-signed-urls-cookies.html

I initially looked at the cookie approach but opted for individually signing each URL inside the HLS manifest file. I use ffmpeg to convert a MP4 file to HLS (e.g. Mp4 to HLS using ffmpeg) and the HLS manifest will be something like: -

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:17
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:16.683333,
my-video0.ts
#EXTINF:8.341667,
my-video1.ts
#EXTINF:8.341667,
my-video2.ts

...

#EXT-X-ENDLIST

You would individually sign each segment e.g.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:17
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:16.683333,
https://cqtgd3b9n5c6qp.cloudfront.net/my-video0.ts?Expires=1609499278&Key-Pair-Id=AIRPEGWQPKAIQ7O3SPLI&Signature=KUvRsV-OpJ014ZQ0dLZF....
#EXTINF:8.341667,
https://cqtgd3b9n5c6qp.cloudfront.net/my-video1.ts?Expires=1609499278&Key-Pair-Id=AIRPEGWQPKAIQ7O3SPLI&Signature=KlVQsz5TVzhEQ8LKs1ZW....
#EXTINF:8.341667,
https://cqtgd3b9n5c6qp.cloudfront.net/my-video2.ts?Expires=1609499278&Key-Pair-Id=AIRPEGWQPKAIQ7O3SPLI&Signature=VR1MBzq~pVsBfOzjZa~M....

...

#EXT-X-ENDLIST

Simple search / replace will dynamically generate this file.

Signing each URL will takes a bit of processing so I add an Expires header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires) to the HTTP response when returning the HLS manifest. This ensures subsequent calls in the browser use the cached copy which massively increases performance.

NOTE - it's important the expires in the HTTP response is (slightly) less than the Expires in the signed URL.

I find individually signing each URL is more secure and you don't need to worry about domains etc but probably slightly more complex (code wise) to implement.

Crosscut answered 30/12, 2020 at 11:34 Comment(0)
F
0

CloudFront signed URLs work great when it is just one file, but like you have discovered it is a problem when you have multiple resources.

For this reason the recommended approach is to use signed CloudFront cookies.

By doing this you only need to sign once to allow all resources from a specific CloudFront distribution and do not need to bother with the signing process on each page load.

Flowery answered 4/6, 2020 at 17:22 Comment(2)
Ahh, Do you have an idea on how to implement that?Tradescantia
Its a similar process to the signed URLs, you use the CLI or SDK. This page has more information on implementation details:docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/…Flowery

© 2022 - 2024 — McMap. All rights reserved.