StackPath Support

How to Setup a Secure Token

The StackPath Secure Token offers a way to secure your HTTP URL's so only users who are sending a valid hashed string in the request will have access to that specific resource.  

Add a Secure Token to a Site

  1. Log into the StackPath Control Panel.
  2. Go to Sites and select Manage to the right of the site you wish to enable the Secure Token for.
    SP_Secure_Token.png
  3. Select Enable Secure Token
  4. The Secure Token link is generated through a script on your origin server with the secret, path and the expiration time as parameters. In the CDN control panel, you only need to specify the Secure Token secret (Referred to as the Secure Key) which is used to decrypt the key in order to validate the Secure Token link.

  5. Usually, secure tokens are created with the following parameters:
    • A secret (Secure Key), that can be any alphanumeric combination of characters (limit of 32 characters). (Example: ju23dff8sf3)
    • The relative path to the file (/file.png)
    • Expiration date of the of the Secure Token link (The link becomes invalid after the specified amount of time, giving the 410 - Gone status response code) 

Secure Token link example: /file.png?st=fKrncCCC2VFLSRmXnIEU0Q&e=1379119860

Sample code

Below are examples of code which can be used on your origin server. This code will generate URLs with included token: 

PHP

<?php
      $secret = 'ErT5!1dFrK0Po';
      $path = '/file.png';
      $expire = time() + 3600; // one hour valid
      $md5 = base64_encode(md5($secret . $path . $expire, true)); // Using binary hashing.
      $md5 = strtr($md5, '+/', '-_'); // + and / are considered special characters in URLs, see the wikipedia page linked in references.
      $md5 = str_replace('=', '', $md5); // When used in query parameters the base64 padding character is considered special.
      $url = "http://site.company.stackpathdns.com{$path}?st={$md5}&e={$expire}"; // use this as DL url

      echo $url;
      echo "\n\n";
      ?>

Python

from time import time
      import base64
      from hashlib import md5

      secret = 'ErT5!1dFrK0Po'
      path = "/file.png"
      expire = int(time()) + 3600
      hashed_string = base64.encodestring(
                        md5(
                          "%s%s%s" % (secret, path, expire)
                        ).digest()
                      ).replace("\n", "").replace("+", "-").replace("/", "_").replace("=", "")
      url = "http://site.company.stackpathdns.com%s?st=%s&e=%s" % (path, hashed_string, expire)

      print url

Ruby 

​require 'base64'
require 'digest/md5'

secret = 'ErT5!1dFrK0Po'
path = "/file.png"
expire = Time.now.to_i + 3600
hashed_string = Base64.encode64(
Digest::MD5.digest(
"#{secret}#{path}#{expire}"
)
).gsub("\n", "").gsub("+", "-").gsub("/", "_").gsub("=", "")
url = "http://site.company.stackpathdns.com#{path}?st=#{hashed_string}&e=#{expire}"

puts url

Java 

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import org.apache.commons.codec.binary.Base64;

public class MakeURL {
/*
* Hi I am a Java class, my hobbies include reducing life-span of keyboards and developers.
* CamelCase all the things
* Needs apache common codecs in build path.. http://commons.apache.org/codec/download_codec.cgi
*/

private static String MakeBinaryHash(String secret, String path, Long expire) throws UnsupportedEncodingException, NoSuchAlgorithmException{
String message = secret + path + expire.toString();
byte[] bytesOfMessage = message.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(bytesOfMessage);
String encoded = Base64.encodeBase64URLSafeString(thedigest);
return encoded;
}

public static void main(String[] args) throws Exception{
String secret = "ErT5!1dFrK0Po";
System.out.println("Secret : " + secret);
String path = "/file.png";
System.out.println("Path : " + path);
Date d = new Date();
Long expire = (d.getTime()/ 1000) + 3600; //Divide by 1000 cause java date is in milliseconds . 1 hour in future
System.out.println("expire : " + expire);
String md5 = "" ;
try {
md5 = MakeBinaryHash(secret, path, expire);
System.out.println("generated md5 : " + md5);
String url = "http://site.company.stackpathdns.com" + path + "?st=" + md5 + "&e=" + expire.toString() ;
System.out.println("genrated url : " + url);
} catch (Exception e){
e.printStackTrace();
}

}

}​

node.js

var md5_digest = require('crypto'); //INCLUDE CRYPTO LIBRARY
var secret = "secure_token"; // YOUR SECURE TOKEN SET IN CP
var path = "/PATH/TO/FILE.EXT";
var d = new Date();
var t = d.getTime() / 1000; // JS TIME IS IN MILLISECONDS SO YOU HAVE TO DIVIDE IT BY 1000
var expire = parseInt(t) + 3600; // EXPIRY IN 3600 SECONDS
var md5 = md5_digest.createHash('md5').update(secret + path + expire).digest('base64'); //MD5 DIGEST
md5 = md5.toString().replace("+","-").replace("/","_").replace(new RegExp("=","gm"),""); // REPLACE SPECIAL CHARACTERS +, /, =
console.log("http://site.company.stackpathdns.com" + path + "?st=" + md5 + "&e=" + expire); //DISPLAY RESULTING URL

javascript on client side

<script type="text/javascript" src="md5.js"></script> //INCLUDE MD5.JS FUNCTION
<script type="text/javascript" src="encdec.js"></script> // INCLUDE FUNCTIONS FOR BASE64 ENCODE/DECODE
<script>
function run(){
var secret = "secure_token";
var path = "/PATH/TO/FILE.EXT";
var d = new Date();
var t = d.getTime() / 1000; // JS TIME IS IN MILLISECONDS SO YOU NEED TO DIVIDE IT BY 1000 TO GET SECONDS
var expire = parseInt(t) + 3600; // ADD EXPIRY OF 3600 SECONDS
var md5 = calcMD5(secret + path + expire); // CALCULATE MD5
var base64 = decodeHex(md5); // HEX DECODE MD5 HASH
var md5base64 = encodeBase64(base64).replace("+","-").replace("/","_").replace(new RegExp("=","gm"),""); //REPLACE SPECIAL CHARS +, /, =

alert("http://site.company.stackpathdns.com" + path + "?st=" + md5base64 + "&e=" + expire); //DISPLAY RESULTING URL
}

.NET/C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace securetokens
{
class Program
{
static void Main(string[] args)
{
var secret = "token";
var path = "/favicon.ico";
//generate unix timestamp and add one day in seconds - expiry time
Int32 unixtimestamp = (Int32)(DateTime.UtcNow.AddDays(1).Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
var expire = Convert.ToString(unixtimestamp);
var md5 = MD5.Create();

//load string into byte array and hash it
byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(secret + path + expire));
//get base64 encoded string
var md = Convert.ToBase64String(data);
//"+","/" and "=" are treated as special characters that need to be replaced/removed acordingly:
md = md.Replace("+", "-").Replace("/", "_").Replace("=", "");
//resulting secure link:
Console.Write("http://site.company.stackpathdns.com" + path + "?st=" + md + "&e=" + expire);
Console.ReadLine();
}
}
}

.NET/VB

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Security.Cryptography
Module Module1

Sub Main()

Dim secret = "token"
Dim path = "/favicon.ico"
'generate unix timestamp and add one day in seconds - expiry time
Dim unixtimestamp As Int32 = CType(Math.Truncate((DateTime.UtcNow.AddDays(1).Subtract(New DateTime(1970, 1, 1))).TotalSeconds), Int32)
Dim expire = Convert.ToString(unixtimestamp)
Dim md5__1 = MD5.Create()

'load string into byte array and hash it
Dim data As Byte() = md5__1.ComputeHash(Encoding.UTF8.GetBytes(secret & path & expire))
'get base64 encoded string
Dim md = Convert.ToBase64String(data)
'"+","/" and "=" are treated as special characters that need to be replaced/removed acordingly:
md = md.Replace("+", "-").Replace("/", "_").Replace("=", "")
'resulting secure link:
Console.Write("http://site.company.stackpathdns.com" & path & "?st=" & md & "&e=" & expire)
Console.ReadLine()

End Sub

End Module

Testing​

Valid Secure Token link
A status code 200 indicates that data has been retrieved successfully:

$ curl -I site.company.stackpathdns.com/foo.pdf?st=YNcnQ1H0tpgCzaLQ&e=1355776589
HTTP/1.1 200 OK
Server: NetDNA-cache/2.2

Expired Secure Token link
A status code 410 indicates that the secure link has expired and access to the data is forbidden:

$ curl -I site.company.stackpathdns.com/foo.pdf?st=YNcnQ1H0tpgCzaLQ&e=1355776589
HTTP/1.1 410 Gone
Server: NetDNA-cache/2.2

Without the Secure Token link
A status code 403 indicates that access to the data is forbidden and no Secure Token was provided:

$ curl -I site.company.stackpathdns.com/foo.pdf
HTTP/1.1 403 Forbidden
Server: NetDNA-cache/2.2
Return to top