Wowza Community

Article: How to protect streaming using SecureToken in Wowza Streaming Engine

You can view the page at How to protect streaming using SecureToken in Wowza Streaming Engine

Hello,

Do you have an example how to generate a hash (wowzatokenhash) in PHP ?

Thanks

Hi,

This issue is being handled in support ticket #122846

Daren

How this secure token will work in case of HLS ABR stream ? does it apply to playlist.m3u8 request only or to chunk requests as well ?

If it doesnt apply to chunk request this schema protect from standard players but not from scripts.

Hello, what is the “wowzatokenCustomParameter=abcdef” - Where do I set it in the config?

Hi,

I don’t know how to create a client page which can able to generate a hash and play the RTMP streams. Could you please provide an example using JWPlayer with RTMP playback ?

Sorry! If it is pretty simple, I didn’t get the logic to create one.

We’ve rolled in production this morning a SecureToken setup after testing it in the last few weeks. Just as a sidenote we are including the client IP in the hash generation. In the test everything was alright, however today we encountered a problem from some of our customers. 403 responses seem to be appearing to random customers: random countries, random ISPs. The 403s are received across diferent wowza instances, for all requests that are being done by those users, even after getting a new IP. Practically some users do not have access to any content served by wowza and secured with SecureToken. After trying to isolate the problem we’ve managed to test with two users having problems on a wowza instance, after inspecting the logs it seems that for some weird reason when calculating the SecureToken wowza is swapping the order of the IP and secret. Practically in our wowza logs I get something like the block below:

ModuleCoreSecurity:string hashed: vod/_definst_/mp4:some.mp4?secret&81.196.30.45&letokenendtime=1427456998&letokensource=web&letokenstarttime=1427449798&letokenuserid=0 
or
ModuleCoreSecurity:string hashed: vod/_definst_/mp4:some.mp4?secret&86.121.74.34&letokenendtime=1427460222&letokensource=web&letokenstarttime=1427453022&letokenuserid=0

which is clearly wrong, and will always fail, because i calculate the hash with …something.mp4?IPaddr&secret&…. Again this only happens for some users, not all, so my securetokenhash is calculated correctly:



Do you have any idea why that would happen or if we can do anything to fix this problem? Needless to say, it is a bit frustrating to not be able to offer a consistent experience to all our users.

I really not clear… HLS from example

$myhash = hash(‘sha256’, “vod/sample.mp4?192.168.1.2&mySharedSecret&myTokenPrefixCustomParameter=abcdef&myTokenPrefixendtime=1500000000&myTokenPrefixstarttime=1395230400”);

hash result is “4e025fb7986c8cac82e517a6fc4a1434fef166fc5ba953e185dd06c48700da8a”.

how to get “A1juf_EJhOjTBgg-HQ5rImiAIXQD7VZDT3dhTlJLoHs=”? :?? :confused::confused:

huh… please update or more detail on document :confused:

here is my example (PHP) (Thanks guys here for some code)

[PHP]

<?php $wowzaContentURL = 'http://patrickz:1935/live/patrickz/playlist.m3u8'; $wowzaContentPath = 'live/patrickz'; $wowzaSecureToken = 'mySharedSecret'; $wowzaTokenPrefix = 'myTokenPrefix'; $wowzaCustomParameter = $wowzaTokenPrefix . "CustomParameter=myParameter"; $wowzaSecureTokenStartTime = $wowzaTokenPrefix ."starttime=". time() ; $wowzaSecureTokenEndTime = $wowzaTokenPrefix ."endtime=". (time() + (7 * 24 * 60 * 60) ); $viewer_ip = '127.0.0.1'; $hashstr = $wowzaContentPath ."?". $viewer_ip ."&". $wowzaSecureToken ."&". $wowzaCustomParameter ."&". $wowzaSecureTokenEndTime ."&". $wowzaSecureTokenStartTime; $hash = hash('sha256', $hashstr ,1); $usableHash=strtr(base64_encode($hash), '+/', '-_'); $url = $wowzaContentURL ."?". $wowzaSecureTokenStartTime ."&". $wowzaSecureTokenEndTime ."&". $wowzaCustomParameter ."&". $wowzaTokenPrefix ."hash=$usableHash"; echo "
";

echo "\nwowzaContentURL = $wowzaContentURL";

echo "\nwowzaContentPath = $wowzaContentPath";

echo "\nwowzaSecureToken = $wowzaSecureToken";

echo "\nwowzaTokenPrefix = $wowzaTokenPrefix";

echo "\nwowzaCustomParameter = $wowzaCustomParameter";

echo "\nwowzaSecureTokenStartTime = $wowzaSecureTokenStartTime";

echo "\nwowzaSecureTokenEndTime = $wowzaSecureTokenEndTime";

echo "
"; echo "\nHash string = $hashstr"; echo "\nHash value = $usableHash"; echo "\nURL = $url"; echo "
"; ?>

[/PHP]

Result

wowzaContentPath = live/patrickz

wowzaSecureToken = mySharedSecret

wowzaTokenPrefix = myTokenPrefix

HLS/HDS is work fine. I tested on jwplayer, grindplayer (OSMF), it work fine. Not test on FlowPlayer yet, but I hope it should work. The URL can’t open on VLC Player.

I have not test on iPhone, iPad and Android yet. also RTMP as well.

Note: I try to disable “Include client ip address in hash generation”, but it not work.

Hope this help you guys.

Basically query parameters without the prefix (eg wowzatoken) are being included in the hash by Wowza. This is surely not the expected behaviour?

I did play around a bit with the secure-token feature and found 2 bugs:

When I want to secure a RTMP-Stream only with the hash (and no other parameters, which should be fine), wowza has problems to decode two == signs at the end of the url.

2014-10-06 14:35:34 CEST comment server INFO 200 - [phzh/definst]ModuleCoreSecurity:hashCalculated: YZR6yljeZRbfQslBzqgomK_ZcCOdjxDviW9NyZKHEHb81cHShICHXnVK-jBN1uUwaB43zVyNNV-s

xNSpyAMlZg== - - - 537047.532 - - - - - - - - - - - - - - - - - - - - - - - - -

2014-10-06 14:35:34 CEST comment server INFO 200 - [phzh/definst]ModuleCoreSecurity:string hashed: phzh/definst/7/a7/7a7c6836-2d87-4e7b-bc93-dddcd93f8457.mov?zZu8 - - - 537047.532 - - - - - - - - - - - - - - - - - - - - - - - - -

2014-10-06 14:35:34 CEST comment server INFO 200 - [phzh/definst]ModuleCoreSecurity:token start time stamp: 0 - - - 537047.533 - - - - - - - - - - - - - - - - - - - - - - - - -

2014-10-06 14:35:34 CEST comment server INFO 200 - [phzh/definst]ModuleCoreSecurity:token end time stamp: 0 - - - 537047.533 - - - - - - - - - - - - - - - - - - - - - - - - -

2014-10-06 14:35:34 CEST comment server INFO 200 - [phzh/definst]SecureTokenDef:Hash YZR6yljeZRbfQslBzqgomK_ZcCOdjxDviW9NyZKHEHb81cHShICHXnVK-jBN1uUwaB43zVyNNV-sxNSpyAMlZg, doesn’t match hash calculated, YZR6yljeZRbfQslBzqgomK_ZcCOdjxDviW9NyZKHEHb81cHShICHXnVK-jBN1uUwaB43zVyNNV-s

xNSpyAMlZg==

when I add a dummy-parameter without the wowzaTokenPrefix, == is taken into account but also the parameter even without the right prefix:

2014-10-06 14:47:44 CEST comment server INFO 200 - [phzh/definst]ModuleCoreSecurity:string hashed: phzh/definst/8/ff/8fff2a89-521e-4a1b-9042-9197132045ac.mp4?g=d&zZu8

but the base64== signs are here:

2014-10-06 14:47:44 CEST comment server INFO 200 - [phzh/definst]SecureTokenDef:Hash 6-VnkVc1Yg1R1dDhEXzSMog43TlQu3Q8mj8MVmZo1xK_uRxX_0plTy60fEdV-8B3X1z6ASso_Jne3Nqt2qI9Yw==, doesn’t match hash calculated, RyVCWjNnqmGh_QawMeUE3I-5ixHRykTAwcaKPWwmlbf5sipAFB3WOp9CSccdQNn-Q1MViZGI6cXJ

Y-chOWKHWQ== - - - 537777.64 - - - - - - - - - - - - - - - - - - - - - - - - -

Do you see the problem?

Are the hashes used in this guide accurate?

String used for hashing (in required alphabetical order):

vod/myInstance/sample.mp4?wowzatokenCustomParameter=abcdef&wowzatokenendtime=1500000000&xyzSharedSecret

RTSP URL sent to server:

rtsp://10.0.2.31:1935/vod/myInstance/sample.mp4?wowzatokenendtime=1500000000&wowzatokenCustomParameter=abcdef&wowzatokenhash=m20I4XSU1Emt zHmz8PbbRsX5OcVi7Km-qI1J3acEV-c=

Using the quoted string above I get a URL safe base64 encoded SHA256 hash as follows:

kJ591xB2lT-X0OA9UdoRx61uwp6A_IoSc_jCx_9h1l8=

This might seem a pedantic question, but if a guide is there to follow then I need a working example.

Can anyone spot anything wrong with this?

Live stream: rtmp://localhost/live/streamer-1/stream

SecureToken prefix: wowzatoken

mySharedSecret: secret

String to hash: live/streamer-1/stream?secret

Base64 (URL safe): _JVKyN33KaexRqoTo2LAbwICidR22K3msulK0pBcWZI=

URL sent to server: rtmp://localhost/live/streamer-1/stream?wowzatokenhash=_JVKyN33KaexRqoTo2LAbwICidR22K3msulK0pBcWZI=

I’m unable to successfully play this live stream. I get a session disconnect in the logs.

I have an interesting situation that I’m not sure if it is possible to support or not. I’m routing the manifest files through our web server and modifying them depending on how they are being played. I’m able to use SecureToken and it works up until the point where the player requests the media chunks. I have the client IP option turned off because I knew that this would cause it to fail, but it is still failing. Is it because the web server and client have different sessions? Any way around this?

We have run into fairly major problems with SecureToken.

If you pause the stream for more than about 20s, the stream session on wowza is destroyed, and the client cannot continue to watch the stream. This is probably because the m3u8 sends fragment links that only contain a session id (so they won’t work if the session is lost), but not the entire wowzatoken… url (which could be used to just start a new session).

This means that right now, there is no good way to protect streams.

Have we overlooked some setting here? Some timeout we could turn up?

We get “Session not accepted” server comments all the time in access.log, this really screws up our streaming :frowning: Has nobody ever noticed this before?

Thanks for any help,

Peter

We had trouble with this - it turned out that the parameters should not be sorted in alphabetical order, they should be sorted via ASCII (or unicode, i guess) sort order. In particular, upper case and lower case did not sort alphabetically, but rather uppercase first, alphabetically, followed by lowercase, alphabetically.

e.g., my token Prefix is Ixxx and my shared secret is fxxxxx. You’d expect the fxxx to sort ahead of the Ixxx if it were truly alphabetical order. However, the sort that worked was:

foo/mp4:sample.mp4?192.168.1.1&Ixxxendtime=1443548637&Ixxxstarttime=1443548037&fxxxxx

When I used the sort() function in perl, I got the correct sort order. When a colleague did a sort with a C# program, it used alphabetical order and it didn’t work.

Here is my perl CGI:

#!/usr/bin/perl -w
use strict;
use Digest::SHA qw(sha256);
use MIME::Base64 qw(encode_base64);
use CGI;
BEGIN {
    use CGI::Carp qw(carpout);
    open( LOG, ">>/opt/apache/logs/cgilog" )    or  die "Can't open log";
    carpout( *LOG );
}
my $rtmpserver='rtmps://rtmps.example.com/';
# note, no leading /
my $videopath='test/mp4:sample.mp4';
# shared between this CGI and the streaming server
my $sharedsecret='f000000000000000';
# Wowza calls this the  "Hash Query Parameter Prefix"
my $prefix='Ixxxxxxx';
my $clientipaddr=$ENV{'REMOTE_ADDR'};
my $now=time();
my $starttime=$prefix . 'starttime=' . $now;
my $plusten=$now + (60*10);
my $endtime=$prefix . 'endtime=' . $plusten;
# the params we are using are the start time, end time, shared secret, and client ip address.
# The secret key must be sorted like the rest.
my @hashparams=($starttime,$endtime,$sharedsecret,$clientipaddr);
@hashparams=sort(@hashparams);
my $hashparams=join('&',@hashparams);
my $tohash= $videopath . '?' . $hashparams;
# we want the BINARY sha256 hash, not the hex-encoded one
my $hash = sha256($tohash) ;
# base-64 encode it
my $hashbase64 = encode_base64($hash) ;
# now make it "safe" base-64 by substituting these two:
$hashbase64 =~ s{\+}{-}g;
$hashbase64 =~ s{/}{_}g;
# seems like we're getting a trailing newline
chomp $hashbase64;
my $finalurl=$rtmpserver . $videopath . '?' . $starttime . '&' . $endtime . '&' . $prefix . 'hash=' . $hashbase64 ;
my $cgi = CGI->new;
print $cgi->header;
print $cgi->start_html(
   -title=>'Video Player Test',
   -script=>{ -type=>'JAVASCRIPT', -src=>'http://www.example.com/jwplayer/jwplayer.js' }
);
print $cgi->h1('Video Player');
print $cgi->p("String to hash is $tohash");
print $cgi->p("final hash is $hashbase64");
print $cgi->p("url is $finalurl");
my $html1='<div id="player"><script type="text/javascript"> jwplayer("player").setup({ sources: [{ file: "';
my $html2='" } ], rtmp: { bufferlength: 3 }, proxyType: "best" }); </script> ';
print $html1, $finalurl, $html2 || die "error in output";
print $cgi->end_html;
exit 0;

Hello,

I have the same question as Patrickz.

I really not clear… HLS from example

$myhash = hash(‘sha256’, “vod/sample.mp4?192.168.1.2&mySharedSecret&myTokenPrefixCustomParameter=abcdef&myTokenPrefixendtime=1500000000&myTokenPrefixstarttime=1395230400”);

hash result is “4e025fb7986c8cac82e517a6fc4a1434fef166fc5ba953e185dd06c48700da8a”.

how to get “A1juf_EJhOjTBgg-HQ5rImiAIXQD7VZDT3dhTlJLoHs=”? :?? :confused::confused:

Php example:


$wowzastart = '0';
$wowzaend = strtotime(date('d-m-Y H:i')) + 1800;
$secret = 'myShareSecret';
$wowzatoken = 'wowzatoken';
$hashstr = hash('sha256', 'vod/_definst_/sample.mp4?'.$secret.'&'.$wowzatoken.'endtime='.$wowzaend.'&'.$wowzatoken.'starttime='.$wowzastart.'', true); # IMPORTANT to set third parameter equals to TRUE
$usableHash= strtr(base64_encode($hashstr), '+/', '-_');

echo "http://xxx.xxx.xxx.xxx/vod/_definst_/sample.mp4/playlist.m3u8?".$wowzatoken."endtime=".$wowzaend."&".$wowzatoken."starttime=".$wowzastart."&".$wowzatoken."hash=".$usableHash."";

Tahir-khalil’s post helped me. Here’s the hash code in VB.NET:

Dim sha256 As System.Security.Cryptography.SHA256Managed = New System.Security.Cryptography.SHA256Managed

Dim sha256Bytes = Encoding.[Default].GetBytes(TokenString)

Dim cryString = sha256.ComputeHash(sha256Bytes)

Dim usableHash = Convert.ToBase64String(cryString).Replace("+", “-”).Replace("/", “_”)

I really not clear… HLS from example

$myhash = hash(‘sha256’, “vod/sample.mp4?192.168.1.2&mySharedSecret&myTokenPrefixCustomParameter=abcdef&myTokenPrefixendtime=1500000000&myTokenPrefixstarttime=1395230400”);

hash result is “4e025fb7986c8cac82e517a6fc4a1434fef166fc5ba953e185dd06c48700da8a”.

how to get “A1juf_EJhOjTBgg-HQ5rImiAIXQD7VZDT3dhTlJLoHs=”? :?? :confused::confused:

From your example you need to adjust your hash paramiter to the following

$string = “vod/sample.mp4?192.168.1.2&mySharedSecret&myTokenPrefixCustomParameter=abcdef&myTokenPrefixendtime=1500000000&myTokenPrefixstarttime=1395230400”;

$hash = hash(‘sha256’, $string, 1);

this will generate the SHA-256 string and output as a BASE64 encoded string.

you then need to sanitise or URL Safe the string.

hop that helps

@Wowza: I agree with these guys the documentation regarding hashing really could use some examples on the acctual generation of the hash

e.g Showing a code example of how to generate the correctly formatted hash in php, classic asp and asp.net would be a good idea.