Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

136 lines (104 sloc) 4.727 kB
== link:index.html[Index] -> link:modules.html[Modules] -> link:modules_handlers.html[Handlers]
Handler: Hidden Downloads
The **Hidden Downloads** handler implements secure download mechanisms.
This module allows to handle temporal URLs to serve hidden files.
|Parameters |Type |Description
|`secret` |String |Required. Share secret between the handler and
the script.
|`timeout` |number |Optional. How long - in seconds - the URL will
be valid.
Technical Description
The idea behind this handler is plain and simple. It will only serve a
file if the URL has been generated by a dynamic execution script that
you've previously written. If the script allows the user to access the
file, it will generate a special encoded URL that Cherokee will handle
through the **Hidden Downloads** module.
If the URL is invalid, is modified, or expires, Cherokee will not
serve the file.
The encoding scheme is quite straightforward. It is basically the
result of MD5-hash of: a shared secret string between Cherokee and the
script, the relative path to the requested file (relative to the rule
document root), and the current time:
'/' HEX (MD5 (Secret + URL + HEX(time))) '/' HEX(time) '/' URL
Here you have a reference implementation in Python:
def secure_download (prefix, url, secret):
import time, hashlib
t = '%08x' % (time.time())
return "/%s/%s" % (hashlib.md5(secret + url + t).hexdigest(), t + url)
The same function written in PHP would be:
function secure_download ($prefix, $url, $secret) {
$time = sprintf('%08x', time());
return "$prefix/".md5($secret.$url.$time)."/$time$url";
It is important to notice that the URLs are only valid for a period of
time. If an URL expires, the server will return an error instead of
the file contents. By default URLs last 60 seconds.
NOTE: Please note that both sample implementations count on having an
URL that starts with `/`.
Lets imagine you have a few ISO files that you want to distribute
among a certain group of people.
First, and most importantly, the ISO files ought to be outside of the
WWW directory root; otherwise, anybody would be able to download
them. Let's imagine those ISO files are located under: `/mnt/isos/`, and
the server document root is located in `/var/www/`.
Now it is time to configure the `/downloads` web directory, so it is
handled by **Hidden Downloads**. We set a shared secret string
(`Abracadabra`), and the document root where the real ISO files are
located (`/mnt/isos`):
.Configuration for directory /downloads
|Handler | `Hidden Downloads`
|Document Root| /mnt/isos
|Secret | Abracadabra
.Hidden downloads configuration
image::media/images/admin_handler_secdownload.png[Hidden Downloads configuration]
To summarize, its a four step process:
- Configure a Directory rule. Let's say: /downloads
- Set the rule to use "Hidden Downloads" handler
- Set the Document Root directory (this is important!)
- Set the Secret string
Next step is to write the logic that will decide what is the user
given access to. For instance, check out this Pyton example:
import time
from hashlib import md5
except ImportError:
from md5 import md5
SECRET = "Abracadabra"
DIR = "downloads"
def secure_download (url, secret):
t = "%08x"%(time.time())
return '/'+ DIR +'/'+ md5(secret + url + t).hexdigest() +'/'+ t + url
# Example request
file = "test.txt"
host = "localhost"
hidden_url = secure_download('/%s'%(file), SECRET)
print "/%(DIR)s/%(file)s -> http://%(host)s%(hidden_url)s" %(locals())
According to this example, if a user tries to access
`/bar/foo/example.iso` and access is granted, working URL such as this
would be provided:
Since the `/downloads` web directory is configured with this "Hidden
Downloads" handler, it will check the URL to ensure that it is valid
and has not expired. Then, if everything was right, it would send the
`/mnt/isos/bar/foo/example.iso` file to the client.
Jump to Line
Something went wrong with that request. Please try again.