from base64 import urlsafe_b64encode from urlparse import parse_qs, urlsplit, urlunsplit from urllib import unquote, urlencode from django.conf import settings from django.core.files.storage import Storage from django.core.files.storage import FileSystemStorage from secret_hash import SecretHash class SecretHash(SecretHash): SECRET_KEY = settings.SECRET_KEY SECRET_LIFESPAN = getattr(settings, 'SECRET_URL_LIFESPAN', 86400) #24h class SecureStorageMixin(Storage): """Append some paramaters to make urls more secure and expire. Made to support nginx and its http_secure_link_module: http://wiki.nginx.org/HttpSecureLinkModule The secure link module is not compiled with nginx by default. If you're using Ubuntu like me, apt-get install nginx-extras to get it. Here's how NGINX can be set up if your secret is "SECRET$KEY123": (NGINX can't natively escape dollar sign literals, so use ${dollar}) geo $dollar { default "$"; } server { ... location /media { secure_link $arg_key,$arg_exp; secure_link_md5 SECRET${dollar}KEY123$arg_exp$uri; # If the hash is incorrect then $secure_link is a null string. if ($secure_link = "") { return 403; } # The current local time is greater than the specified expiration. if ($secure_link = "0") { return 403; } ... } } """ def url(self, name): # Take the URL generated by super and break it up u = super(SecureStorageMixin, self).url(name) scheme, netloc, path, query_string, fragment = urlsplit(u) query_params = parse_qs(query_string) # Generate the extra parameters and reconstruct the URL. hash = SecretHash(unquote(path)) query_params['key'] = [hash.key] query_params['exp'] = [hash.expires] new_query_string = urlencode(query_params, doseq=True) return urlunsplit((scheme, netloc, path, new_query_string, fragment)) class SecureFileSystemStorage(SecureStorageMixin, FileSystemStorage): pass