Securing Video Streams – Teaming Techniques
Content protection has been a major issue since the beginning of the digital era and its importance is only growing.
Services like Netflix, Hulu, Amazon Video, Spotify, Apple Music etc base their entire business model on the delivery of digital media to end users. The more securely these services can protect their streams, the more secure they can feel about their revenue streams.
In this post we review common methods of stream protection and discuss how they can be used in combination to create a reasonably secure streaming infrastructure.
Why do I need to protect my streams?
“I have a website, I point my player to my server, and that’s it. Right?”
If only this were true.
To paraphrase Hunter S. Thompson:
“The [internet] is a cruel and shallow money trench, a long plastic hallway where thieves and pimps run free, and good men die like dogs. There’s also a negative side.”
The internet is a chock full of malicious activities and circumvention. If you provide a service that can be easily copied or stolen, someone will likely take advantage of that and piggyback on your hard work (and your hosting bill) to offer the service as his own.
This would result in the following:
- fewer users will visit your website to watch the content since they have alternative options
- you will be paying for users to stream content on someone else’s site
- users who do visit your site will experience a degraded service because your servers will be overloaded by the site that has stolen your content
It’s the perfect storm
Here are some examples of how you might get screwed if you’re not properly protected:
- Another website hotlinks or embeds your content on its own site.
- An automated program scrapes the video info from your page and previews it on an external platform.
- Users download your content and redistribute it on other channels (share the stream URL, etc)
How To Defend Yourself
There are different methods for addressing different aspects of security and a combination of them usually provides added security.
Let’s examine them one by one:
- Origin blocking (Origin, CORS, Referrer)
- Embed busting
- Tokenized URLs
- Generic token
- Advanced user specific tokens
- Login/Paywall
- Stream encryption
- DRM (Digital Rights Management)
Hardware Approach: Origin Protection

Since every video (like every image) has a URL, in theory someone could simply copy your video URL and use it with a player of their own choice on a different site.
This means that you are now involuntary providing media services to freeloaders. They might constitute only a small portion of your traffic, but if you don’t address this issue, it might grow to leech most of your resources.
How to protect against hotlinking:
- Block requests with an invalid referrer
- Create a verification request
- Use an explicit domain instead of
*
in CORS headers- For example:
Access-Control-Allow-Origin: my-domain.com
.
Note that this will not have any effect if the browser plays the video directly and not via Javascript (e.g.HLS
on Safari for iOS,DASH
on Chrome for Android, etc) - example CORS configs
- For example:
- Restrict the domains your server accepts
Another way: Embed Buster
It’s pretty common to use nested frames within your page (iframe
), especially to play a video, as this reduces the load time for the main page and eases deployment. With this approach, every time you want to place a video in your page, all you have to do is embed the player frame and point it to the desired video.
But what prevents a different site from taking your iframe
and embedding it? Even if you don’t use an iframe
, you still might be vulnerable to cross-site embedding.
How to protect your page against cross-site embedding:
Built in X-Frame-Options
header
If your iframe
has the same domain as the parent (top) page, use SAMEORIGIN
.
Use DENY
if you don’t want your page to be embedable by anyone.
It’s also possible to add this header conditionally based on the referrer.
X-Frame-Options: DENY
// or
X-Frame-Options: SAMEORIGIN
Javascript based embed buster
This snippet will navigate the user back to your page if someone tries to iframe
you. If you place it at the very top of your page, it will make your page completely non-embedable (not even by you).
<script>
if (top != self) { top.location.replace(self.location.href); }
</script>
Tokenized URLs
Some media servers employ a token system that blocks requests unless a specified token is provided. This token is very similar to CSRF – it will be a cryptic hash that encapsulates certain information and will usually have a short expiry time to prevent extended usage.
Generic tokens
A common implementation for a generic token might look like this.
http://example.com/hls/playlist.m3u8?token=4180da90a6973bc8bd801bfe49f04a&expiry=1526231040535
http://example.com/hls/segment001.ts?token=4180da90a6973bc8bd801bfe49f04a&expiry=1526231040535
And so trying to get http://example.com/hls/segment001.ts
will result in 403
or other error status.
The good:
- Time limited
- Prevents static hotlinking
The bad:
- Can be shared
- Can be scraped
Advanced user specific tokens
It’s possible to create a session based token that will be more tightly locked to a specific user. While a general token might prevent direct access to the resource, a session token will prevent access to the resource outside the context of your site. The token might validate that the user requesting the stream has the same IP, User-Agent, JS generated hash and other user specific info.
That means that unlike other protection mechanisms, having the stream url will not allow the holder to view the stream.
Examples:
- nginx secure link
- wowza secureToken
- Cookies based authentication (native playback only – e.g. HLS on Safari/iOS)
- Advanced tokens (Akamai SecureHD (pdf))
The Ever Popular Login / Paywall
Another way to protect your stream is to prevent access to your stream URL in the first place.
Streams that are behind a user login and paywall are much less likely to be scraped / played externally and, when combined with user specific tokens, can provide a fair level of protection.
The Good:
- Reduces unwanted public exposure of your stream
- Streaming authentication is tied to an actual user session
The Bad:
- More complex
- Increases engagement barrier
Stream Encryption

Also known as AES
encryption, Stream Encryption means that downloaded segments are a scramble of bytes that need to be decrypted using a cryptographic key before becoming usable.
While this might sound very protective, it doesn’t provide any protection benefits in terms of content theft when used as is, and is merely an additional technical obstacle.
In HLS for example, the key is provided alongside the segments list (manifest file) which means your stream bandit can easily decrypt the segments with less than 10 lines of code.
Then what is Stream Encryption good for?
Let’s say you have a server that authenticates users and only serves the manifest file (e.g. .m3u8
, .mpd
) and you have all of your security mechanisms setup on that server.
Does the server that serves the actual video segments also need authentication?
By using Stream Encryption you can enforce that all users have to go through your playlist server and through the security logic residing there to get the decryption key. This means that the segment servers can operate without any special protection because the segments are not usable without the key.
Another use case for Stream Encryption would be content protection. If you are using a third party delivery service that shouldn’t be allowed to watch the content, you can encrypt the content and serve the playlist elsewhere.
If the motivation for using stream protection arises due to security while in transport, HTTPS should be used instead which will provide a secure tunnel between your servers and the end user.
Hello DRM! (igital Rights Management)
DRM is currently the most secure way to deliver digital content over the internet. While DRM is similar in concept to Stream Encryption, it separates the decryption key from the content and the entire decryption flow is managed in a secure black box which is not exposed to user-land and, consequently, not vulnerable to user-land hacks and breeches.
This black box decryptor is created by large companies such as Google, Apple, Microsoft etc that incorporate state of the art cryptographic tools, as well as hardware assisted decryption which renders external decryption improbable.
Common DRM products:
- Widevine (Google) – Chrome, Firefox, Android
- FairPlay (Apple) – Safari, iOS
- PlayReady (Microsoft) – Edge, IE, Windows Phone
Key points:
- Decryption is managed by the browser
- External license server
The problem:
- Costs money
- Adds complexity
Example Secure Architecture
A basic implementation of a media server and a web player with multiple defense mechanisms. With Nginx as the media server and Clappr will be used as the web player. Handy here especially because both are free of charge.
The implementation incorporates the following mechanisms:
- Domain filtering
- Referrer filtering
- Embed buster
- Session token
- AES encryption
Full implementation:
# Secured HLS setup with Nginx as media server
This is an example for an HLS delivery with basic security.
Nginx compiled with nginx-rtmp-module & secure-link is used as media server.
Features:
- Domain filtering
- Referrer filtering
- Embed buster
- Session token for playlist, segments and AES keys
- AES encryption
- HTTPS only
Throughout this example the host is assumed to be `example.com`.
if you want to use this configurations, be sure to replace all instances of `example.com` with your domain.
### Compiling Nginx
```sh
# install deps (Ubuntu)
sudo apt-get install -y build-essential libpcre3 libpcre3-dev libssl-dev
wget http://nginx.org/download/nginx-1.10.1.tar.gz
tar -xf nginx-1.10.1.tar.gz
cd nginx-1.10.1
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module --with-http_secure_link_module
make -j
sudo make install
# nginx is now installed in /usr/local/nginx
```
### Pushing video to Nginx
In order to push video to nginx i'm going to use ffmpeg which well supports RTMP as its output.
I'm going to create an `I frame` roughly every 2 seconds which will allow nginx to achieve the 4s segment target.
For simplicity i'll be using a static mp4 file and ingest it in infinite loop.
```sh
ffmpeg -hide_banner \
-stream_loop -1 \
-re -i test-video.mp4 \
-c:a aac -c:v h264 -g 48 \
-f flv rtmp://localhost:1935/show/live
```
I'm using `live` as the stream name, the output hls will carry that same name - e.g. `live.m3u8`.
### Generating Session token
The session token is based on this format (note the spaces):
`MD5("EXPIREY_DATE_IN_SECONDS CLIENT_IP_ADDRESS SECRET")`
here are several examples of generating the token:
BASH
```sh
get_customer_url() {
local IP=${1:-127.0.0.1}
local SECRET=${2:-VERY_COOL_SECRET}
local EXPIRES="$(date -d "today + 30 minutes" +%s)";
local token="$(echo -n "${EXPIRES} ${IP} ${SECRET}" | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =)"
echo "https://example.com/video/hls/${token}/${EXPIRES}/live.m3u8"
}
get_customer_url 10.20.1.55 "uigfp(@#tfpIUDGPFiouGDF"
```
Node.JS (Javascript)
```js
var crypto = require('crypto');
function generateSecurePathHash(expires, client_ip, secret) {
if (!expires || !client_ip || !secret) throw new Error('Must provide all token components');
var input = expires + ' ' + client_ip + ' ' + secret;
var binaryHash = crypto.createHash('md5').update(input).digest();
var base64Value = new Buffer(binaryHash).toString('base64');
return base64Value.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}
function getStreamUrl(ip, secret) {
const expiresTimestamp = new Date(Date.now() + (1000 * 60 * 30)).getTime();
const expires = String(Math.round(expiresTimestamp / 1000));
const token = generateSecurePathHash(expires, ip, secret);
return `https://example.com/video/hls/${token}/${expires}/live.m3u8`;
}
getStreamUrl('127.0.0.1', 'uigfp(@#tfpIUDGPFiouGDF');
// https://example.com/video/hls/LdS-kcC-JGVHGNTFlX-6Sw/1526373776/live.m3u8
Chunked out
Protecting streams is not easy to do. However, you can apply a few simple mechanisms in combination to protect yourself against the most common risks.
There will always be a way to steal content – for example, by capturing the screen whilst the video is playing. But, even for that scenario, some solutions start to emerge. For example, some distributors already use a watermarking technique that enables tracing leaked content back to its source, thus pointing out the culprit.
Digital content (video, music, e-books, games, etc) is becoming more and more common, so the need for protection will only grow. It only makes sense that DRM will evolve and become much more secure and much cheaper soon, so be sure to stay updated with the advancements in this subject.
DRM might not be viable for your current implementation for various reasons, but preventing hotlinking, embedding, scraping and securing transport are a must.