Archives
Visitors
  • 40845This month:
  • 204Today:
  • 13Currently online:



LeaseWeb CDN

“HTTP secure link” using OpenResty Nginx module

Whenever you generate a link on your website to let your visitor download some content, you run into the risk that people will start sharing that link with others. If you want to discourage that you can require the user to login before downloading. If this is not an option for you then you can generate links that are only valid for a few seconds. This is a feature that Nginx supports using the Nginx module HttpSecureLinkModule.

Standard HttpSecureLinkModule

You can specify a Unix timestamp on which the link should no longer be valid; you add this timestamp to the query string like this:

http://localhost/package.zip?e=1402056061&s=be0c677337fec22820b916e48c54755e

In this URL (that uses ‘GET’ parameters) the ‘e’ parameter is the expiry timestamp and the ‘s’ parameter is the signature. This signature is calculated by taking a secret and appending the expiry timestamp to it and calculating a hash over that.

$signature = md5( $secret . $path . $expiry );

OpenResty Nginx modules

But what if you want to use a variant on that? For instance, if you want to restrict on certain networks or use a different hashing algorithm for the signature? My idea was to use the excellent building blocks from the set-misc-nginx-module that is made by Yichun Zhang (a.k.a. “agentzh”). This Nginx module is not included in Nginx by default, but is a part of the OpenResty Nginx distribution or as Yichun Zhang explaines:

By taking advantage of various well-designed Nginx modules, OpenResty effectively turns the nginx server into a powerful web app server. OpenResty is not an Nginx fork. It is just a software bundle. – openresty.org

We did 3 pull requests to the “set-misc-nginx-module” that allow for the needed functionality. You can compile this Nginx module into your Nginx by pointing the “–add-module” configure option to your checkout of this module. We have added the “set_encode_base64url”, “set_decode_base64url”, “set_expired” and “set_ip_matches” directives for building a custom secure link Nginx module.

Installation instructions

The configuration for the Nginx module would be like this:

# GET /?e=1893452400&n=MC4wLjAuMC8w&s=QVvNrcn-j4smvOFhuPTUU7EgoI8
# returns 403 when signature on the arguments is not correct OR
# when expire time is passed or network does not match.
# It is an alternative to the HttpSecureLinkModule in Nginx.
# This example has expiry "2030-01-01" and network "0.0.0.0/0".
location / {
  set_hmac_sha1 $signature 'secret-key' "e=$arg_e&n=$arg_n";
  set_encode_base64url $signature;
  if ($signature != $arg_s) {
    return 403;
  }
  set_expired $expired $arg_e;
  if ($expired) {
    return 403;
  }
  set_decode_base64url $network $arg_n;
  set_ip_matches $ip_matches $network $remote_addr;
  if ($ip_matches = 0) {
    return 403;
  }
  root /var/www;
}

Where the PHP code to generate the link would be:

<?php
function base64url_encode($s)
{ return rtrim(strtr(base64_encode($s),"+/", "-_"),"=");
}
$secret = "secret-key";
$expiry = strtotime("2030-01-01 00:00:00");
$network = base64url_encode("0.0.0.0/0");
$query = "e=$expiry&n=$network";
$signature = base64url_encode(hash_hmac('sha1',$query,$secret,true));
$url = "http://localhost/?$query&s=$signature";
echo "$url\n";

Output would be:

http://localhost/package.zip?e=1893452400&n=MC4wLjAuMC8w&s=QVvNrcn-j4smvOFhuPTUU7EgoI8

11 Responses to ““HTTP secure link” using OpenResty Nginx module”

  • campones:

    hello,

    This looks pretty similar to what I am trying to achieve

    I m following this nginx secure link how to

    http://nginx-rtmp.blogspot.fr/2013/06/secure-links-in-nginx-rtmp.html

    But the owner doesn’t provide a php script to make it working, beside his bash command..

    I d need some help creating the right php script to produce the right hash to secure my videos, do you have any ideas?

    thanks

  • Maurits van der Schee (Innovation Engineer):

    @campones: Here you go:


    $mysecretkey = "mysecretkey";
    $path = "myapp/mystream";
    $expiry = strtotime("+1 hour");
    $b64 = base64_encode(md5($mysecretkey.$path.$expiry,true));
    $b64u = rtrim(str_replace(array('+','/'),array('-','_'),$b64),'=');
    echo "rtmp://localhost/$path?e=$expiry&st=$b64u\n";

    Hope it helps.

  • campones:

    Hello

    I have been testing your code and it works great! Can’t thank you enough!

    Hope it s okay if I link you back on that forum to share your solution, might help many others..

  • Maurits van der Schee (Innovation Engineer):

    @campones: I’m glad it helped you. Please add the link. I’m happy to help others as well..

  • Miguel G.:

    Hi Campones, I have tried your solution but I can’t make it work. Any help is appreciated. This is my confs:

    location /on_play {

    # set connection secure link
    secure_link $arg_st,$arg_e;
    secure_link_md5 mysecretkey$arg_app/$arg_name$arg_e;

    # bad hash
    if ($secure_link = “”) {
    return 501;
    }

    # link expired
    if ($secure_link = “0”) {
    return 502;
    }

    return 200;
    }

  • Maurits van der Schee (Innovation Engineer):

    @Miguel: What is the behavior you see?

  • Miguel G.:

    Hi Maurits, I have used the php suggested and the nginx conf suggested by the autor of rtmp (cited above) without changes but the link that have been generated doesn’t play. The url of the link generator: http://serviprint.com.mx/test/secure1.php My nginx log:

    199.185.73.186 [31/Dec/2014:04:40:20 -0500] PLAY “vod” “mp4:vid3.mp4″ “e=1420022327&st=LLsbajEG5UzBOWmSXvBWgA” – 502 292 “http://www.jwplayer.com/wizard/” “WIN 15,0,0,246″ (0s)
    127.0.0.1 – – [31/Dec/2014:04:40:20 -0500] “POST /on_play HTTP/1.0″ 501 180 “-” “-” “-”

    Thanks a lot!

  • Miguel G.:

    I have no knowledge about php but I’m wondering the reason it’s not working is because the default nginx conf doesn’t have anything about the expiry instruction that it’s included in the php link generator.

    location /on_play {
    secure_link $arg_st,$arg_e;
    secure_link_md5 mysecretkey$arg_app/$arg_name$arg_e;

  • Maurits van der Schee (Innovation Engineer):

    @Miguel: That does not seem to be the problem to me, but I do not see what you are doing wrong.

  • Miguel G.:

    Hi Maurits, thanks for your help. I have founded another website with some code and I’ve had better luck, but I could only make it work for http, not for rtmp. The code looks very similar to what you have provided. It could be great it you could give us some help to make it work for RTMP.
    Here is the Nginx conf and the php code: http://pastebin.com/678mj3TL
    Thanks in advance.

Leave a Reply