Private HTTP Live Streaming via CloudFront
Asked Answered
N

3

6

I am working on an iOS app which allows downloading and HTTP live streaming of private videos. The videos are stored in an Amazon S3 bucket (as mp4 and segmented as m3u8/ts files). Also CloudFront is turned on and connected to the bucket.

Since the content is private, I need to sign the URLs when connecting via CloudFront. In order to sign the URLs it's necessary to use the private key and therefore it's not possible to generate signed URLs in the iOS app without storing the private key in the bundle. And that would be a bad idea!

So I decided to write a simple Ruby server, which performs the URL signing and redirects to the generated signed CloudFront URL as follows:


http://signing.server.local/videos/1.mp4https://acbdefg123456.cloudfront.net/videos/1.mp4??Expires=XXX&Signature=XXX&Key-Pair-Id=XXX

http://signing.server.local/videos/1.m3u8https://acbdefg123456.cloudfront.net/videos/1.m3u8??Expires=XXX&Signature=XXX&Key-Pair-Id=XXX


For video downloads it works well, since there is only one request. But when I want the content streamed and give the MPMoviePlayerController the URL of the signing server, only the first request is signed by the server and redirected to CloudFront. For the next requests the MPMoviePlayerController takes the first signed CloudFront URL as the base and tries to connect directly without going throw the signing server.

The paths in the m3u8 files are relative.

Any suggestions how to implement this feature without the need to send all the content through the signing server?

Nailbiting answered 10/10, 2013 at 8:15 Comment(0)
N
9

The correct way to do private HLS with S3/CloudFront or any other storage/CDN is to use HLS encryption. See the Apple documentation about this topic.

In addition to the storage where your playlists and segmented video files are stored you have to integrate a secure HTTPS server for storing the top level playlists and keys. These keys are generated during the segmenting using the Apple HLS tools.

Here is how it works:

  1. The MPMoviePlayerController gets an URL pointing to the top level playlist (.m3u8) on the secure HTTPS sever.
  2. In this file there are links to the variant playlists (prog_index.m3u8) which are stored in S3/CloudFront and which point to the video files (.ts).
  3. Additionally the variant playlists contain a link to the keys which are necessary in order to read the video files. These keys are stored on the secure HTTPS server as well.

See the following image:

Protecting HLS Keys

Taken from the presentation Mobile Movies with HTTP LIve Streaming (CocoaConf DC, Jun '12)

Of course there are possibilities to make the infrastructure more secure, see the linked Apple documentation.

I also created a Ruby script for segmenting to produce the output with given base URLs, which makes things a lot simpler.

Nailbiting answered 25/1, 2014 at 10:36 Comment(2)
This answer came up at the top of my search regarding securing HLS videos using cloudfront. I think it is a bit out-of-date, so I thought I would amend the answer by pointing out that secure cookies are one of the main AWS example use cases for restricting access to HLS on cloudfront: docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/…Duer
As Tatyree points out, secure cookies are advised for browsers like Safari. I write up a solution for another issue, it can be found as a comment / answer https://mcmap.net/q/1194958/-stream-aws-s3-hls-videos-in-ios-browsers on that post there. hope it helps.Zwickau
S
2

Lukas Kubanek has the right answer. However, you can get the effect of signed URLs by putting the top-level playlists in a "private" bucket, and then putting all the other playlists and .ts files in a public bucket. This is pretty much as secure as using signed URLs for everything, in that anyone who wants to can still download and save the content, but can't merely share the URL they were given. They can of course open the top-level playlist and then share a single stream of their choice, or host the top-level playlist themselves, but it's at least a small level of security-by-obscurity that may be enough for your content. Also, if you sign every single segment, you run into a problem with content that's longer than your time limit, or with the user simply pausing the video until the segment links expire.

Spurlock answered 5/2, 2014 at 19:23 Comment(1)
I would love a walk-through of how this is done. For example, is the .m3u8 playlist file created by pointing to the files in the other buckets like http://d17hw8d9njib15.cloudfront.net/somefile/hls400k.m3u8?Cheke
A
0

I think you need some way to avoid doing two requests to different servers for each chunk of video.

Possible solution: Could you change the Cloudfront private key every few minutes? If yes, then just authenticate however you want (bidirectional handshake) and send the app the current private key. If it expires, or if there are any errors due to it expiring at not exactly when expected, just re-authenticate and get new private key.

Possible solution: Talk to authentication server when you want to play video X, and get signed URLs for every part of that video, or even better: a m3u8 file containing signed URLs. Then, play those directly...

Possible solution: Run everything through a local proxy (on loopback interface on the iOS device). Then modify request URLs as needed, or make them redirects.

Arbitrament answered 1/11, 2013 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.