This article discusses the various options to provide security to a Pull Zone website. These options include referrer access control and use of secure tokens, as well as tests to confirm these features are working as intended.
This tutorial assumes a Pull Zone has already been created.
Referrer Access Control
Referrer Access Control feature allows you to control which domains can refer to your CDN assets by either Whitelisting or Blacklisting specific domains. You can define which referrers would be strictly allowed or disallowed to access your CDN assets by adding them into provided box.
If you are using the Whitelist feature make sure to have your domain whitelisted as well, otherwise your site won't be able to pull files from the CDN and populate pages properly.
After selecting the desired option (Whitelist/Blacklist) you can input the domains for which should be included in the selected type of list. Please note that both Whitelist and Blacklist cannot be used at the same time.
Because we only allow a maximum of 255 characters, make sure to use the “*” wildcard to include subdomains in the whitelist.
Nginx Block:
server {
...
location / {
# This is the list of BLACKLISTED referers
valid_referers *.spammer.com spammer.com *.leecher.com;
if ($invalid_referer != "1" ) {
return 403;
}
...
}
Secure Token
To enable a secure token, a "Secure Key" must be provided to define the validity of the token. Please note that this key has a limit set to 32 characters. To properly use the secure token, a separate file must be used that will combine the URL hash and time stamp:
Sample Code
-
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://foo.bar.netdna-cdn.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://foo.bar.netdna-cdn.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://foo.bar.netdna-cdn.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://foo.bar.netdna-cdn.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://ZONENAME.ALIAS.netdna-cdn.com" + path + "?st=" + md5 + "&e=" + expire); //DISPLAY RESULTING URL
-
javascript on client side. The md5.js and encode-decode.js libraries are necessary to handle hashing and encoding.
<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://ZONENAME.ALIAS.netdna-cdn.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://ZONE.ALIAS.netdna-cdn.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://ZONE.ALIAS.netdna-cdn.com" & path & "?st=" & md & "&e=" & expire) Console.ReadLine() End Sub End Module
Test the zone file accessibility:
~$ curl -I foo.bar.netdna-cdn.com/file.png
HTTP/1.1 403 Forbidden
Server: NetDNA-cache/2.2
Expected Results
~$ curl -I foo.bar.netdna-cdn.com/file.png?st=fKrncCCC2VFLSRmXnIEU0Q&e=1379119860
HTTP/1.1 200 OK
Server: NetDNA-cache/2.2
We hope this article was helpful and as always, If there are any questions or concerns about any of the topics mentioned in this article, please feel free to reach out to support - we are available 24/7 by chat or email!