Reputation: 1497
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?
Upvotes: 6
Views: 8120
Reputation: 3636
This is a good AWS article to help you decide between (1) cookies and (2) signed URLs: -
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.
Upvotes: 1
Reputation: 35188
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.
Upvotes: 0