Secure Token

Token authentication ensures that a URL is only accessible during a defined Unix time. You can define the time frame with an exact expiration time. Once a token has expired, it is not possible anymore to access the content. Several tokens with different expiration times can be created for the same file. A 403 Forbidden will be return if the token is not valid and a 410 Gone if the secure token has already expired.

Format

http://yourzone-id.kxcdn.com{path}?token={token}&expire={timestamp}

Setup Token Authentication

  1. Create a push or pull zone. If you use a pull zone for token authentication, make sure the origin URL is not public and it can’t be easily guessed.
  2. Enable Secure Token in zone settings.
  3. Define a Secure Token Key. This key will be needed to generate the token.
  4. Use one of the following code examples to generate the token (e.g. from your website).

PHP

<?php
	$secret = 'securetokenkey';
	$path = '/path/to/file1.jpg';

	// Expiration in seconds (e.g. 90 seconds)
	$expire = time() + 90; 

	// Generate token
	$md5 = md5($path.$secret.$expire, true);

	$md5 = base64_encode($md5);
	$md5 = strtr($md5, '+/', '-_');
	$md5 = str_replace('=', '', $md5);  

	// Use this URL
	$url = "http://yourzone-id.kxcdn.com{$path}?token={$md5}&expire={$expire}"; 

	echo $url;
?>

Python

import base64
from hashlib import md5
from time import time

secret = 'your_secret'
path = "/path/to/file1.jpg"

# expiration in seconds (e.g. 180 seconds)
expire = int(time()) + 180

# generate token
token = base64.encodestring(
                                md5(
                                    "%s%s%s" % (path, secret, expire)
                                ).digest()
                            ).replace("\n", "").replace("+", "-").replace("/", "_").replace("=", "")
secured_url = "http://demo-1.kxcdn.com%s?token=%s&expire=%s" % (path, token, expire)

# return secured URL
print secured_url

Ruby

require 'digest/md5'
require 'base64'

secret = 'your_secret'
path = '/path/to/your/file.jpg'

# expiry time in seconds (e.g. 3600 seconds)
expire = Time.now.to_i + 3600
token = Base64.encode64(
                            Digest::MD5.digest(
                            "#{path}#{secret}#{expire}"
                            )
                        ).gsub("\n", "").gsub("+", "-").gsub("/", "_").gsub("=", "")

# final secured URL
url = "http://demo-1.kxcdn.com#{path}?token=#{token}&expire=#{expire}"

puts url

node.JS

var crypto = require('crypto'),
    secret = 'your_secret',
    path = '/path/to/your/file.jpg';

// define expiry (e.g. 120 seconds)
var expire = Math.round(Date.now()/1000) + 120;

// generate md5 token
var md5String = crypto
  .createHash("md5")
  .update(path + secret + expire)
  .digest("binary");

// encode and format md5 token
var token = new Buffer(md5String, 'binary').toString('base64');
token = token.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');

// return secure token
console.log('http://demo-1.kxcdn.com' + path + '?token=' + token + '&expire=' + expire);

Java

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

public class getSecureURL {

    // generate token
    private static String getBinaryToken(String path, String secret, Long expire) throws UnsupportedEncodingException, NoSuchAlgorithmException{
        
        String urlData = path + secret + expire.toString();
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] messageDigest = md.digest(urlData.getBytes());
        String token = Base64.encodeBase64URLSafeString(messageDigest);
        
        return token;
        
    }

    // main method
    public static void main(String[] args) throws Exception{
        
        String secret = "your_secret";
        String path = "/path/to/your/file.jpg";
        
        Date date = new Date();
        // expiry time (e.g. 300 seconds)
        Long expire = (date.getTime()/1000) + 300;
        String token = "" ;
        
        try {
            md5 = getBinaryToken(path, secret, expire);

            // final secured URL with token and expire variables
            String url = "http://demo-1.kxcdn.com" + path + "?token=" + md5 + "&expire=" + expire.toString() ;
            System.out.println("genrated url : " + url);
        } catch (Exception e){
            e.printStackTrace();
        }

    }

}

Example of how the link will look like:
http://yourzone-id.kxcdn.com/path/to/file1.jpg?token=85b9a81b78b24b4d18303c91b79e0124&expire=1384719072

Generate a Secure Token with openssl

echo -n '{path}{secret}{timestamp}' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =

Example

echo -n '/path/to/file1.jpgmysecret1384719072' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =

16 Comments

        1. KeyCDN

          We currently don’t have an ETA for this feature. How about using Hotlink Protection (www.keycdn.com/support/create-a-zonereferrer/)? We will announce any new feature in our blog and on Twitter.

    1. Sven

      Secure token restricts the access based on the query string parameters. Blocking access by IP would require a custom rule. Please open a support request in that case.

      1. Laci

        Actually, I wanted to know if the IP can be encoded into the query string. I’m looking for a replacement for Cloudfront and there both the access time and the client’s IP can be included into the token.

  1. Andreas Schuldei

    i see you use md5. https://en.wikipedia.org/wiki/MD5 here it says there are collision attacks.

    “Also in 2004 more serious flaws were discovered in MD5, making further use of the algorithm for security purposes questionable; specifically, a group of researchers described how to create a pair of files that share the same MD5 checksum.[6][7] Further advances were made in breaking MD5 in 2005, 2006, and 2007.[8] In December 2008, a group of researchers used this technique to fake SSL certificate validity.[9][10] As of 2010, the CMU Software Engineering Institute considers MD5 “cryptographically broken and unsuitable for further use”,[11] and most U.S. government applications now require the SHA-2 family of hash functions.[12] In 2012, the Flame malware exploited the weaknesses in MD5 to fake a Microsoft digital signature.”

    can you please provide another, better cypher?

  2. Andreas Schuldei

    What is the recommended way of delivering part of your content with secure token and some without? have two different zones?

  3. Oleg Makogon

    I use Nimble media server as origin live HLS.
    After enabling Secure Token, my live stream stopped playing. I can download playlist.m3u8, i.e., Secure Token authorizes access to it.

    playlist.m3u8 has the contents:
    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-STREAM-INF:BANDWIDTH=197116,RESOLUTION=640×480,CODECS=”avc1.42001f,mp4a.40.34″
    chunks.m3u8

    And player reports this error:
    access_http error: error: HTTP/1.1 403 Forbidden
    main error: open of `http://hls-xxxx.kxcdn.com/app/111222live/chunks.m3u8′ failed

    Is there a way to play the stream, for which playlist.m3u8 has such content?

  4. Batiste Bieler

    I cannot get it work with any query parameters: eg.

    mydomain.com/crop?token=34mXkHLNoSUoRgsOV13UrA&expire=1498471646

    This fails to authenticate

    mydomain.com/crop?blop=1&token=KBY2lDl7fX4W2Sx3e-LK6g&expire=1498471826

Leave A Comment?