Here are a couple of Django decorators for limiting access to a view based on the request's HTTP_REFERER
. Both raise a Django PermissionDenied
exception if the referer test fails (or a referer simply isn't provided).
The first, referer_matches_hostname
, takes a hostname (and port, if specified) and matches it against the referer's. If multiple arguments are supplied a match against any of the hostnames will be considered valid.
The second, referer_matches_re
, takes a regex pattern (like Django's urlpattern) and tests if it matches the referer. This is obviously more flexible than referer_matches_hostname
providing the ability to match not just the hostname, but any part of the referer url.
Finally there's an simple example decorator, local_referer_only
, that limits a view to the current site by using Django's django.contrib.sites
to look up the current hostname.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | from django.core.exceptions import PermissionDenied
def referer_matches_hostname(*netlocs):
"""
Decorator for views that checks that if the request's HTTP_REFERER matches
the supplied string. Failure raises a PermissionDenied exception. If
multiple arguments are supplied the decorator will try to match any of
them.
"""
def _dec(view_func):
def _check_referer(request, *args, **kwargs):
import urlparse
referer = request.META.get('HTTP_REFERER', '')
referer_netloc = urlparse.urlparse(referer).netloc
if referer_netloc in netlocs:
return view_func(request, *args, **kwargs)
raise PermissionDenied()
_check_referer.__doc__ = view_func.__doc__
_check_referer.__dict__ = view_func.__dict__
return _check_referer
return _dec
def referer_matches_re(regex):
"""
Decorator for views that checks that if the request's HTTP_REFERER matches
the supplied regex pattern. Failure raises a PermissionDenied exception.
"""
import re
regex = re.compile(regex)
def _dec(view_func):
def _check_referer(request, *args, **kwargs):
referer = request.META.get('HTTP_REFERER', '')
if regex.match(referer):
return view_func(request, *args, **kwargs)
raise PermissionDenied()
_check_referer.__doc__ = view_func.__doc__
_check_referer.__dict__ = view_func.__dict__
return _check_referer
return _dec
from django.contrib.sites.models import Site
local_referer_only = referer_matches_hostname(str(Site.objects.get_current()))
## Same, but using referer_matches_re:
# regex = r'^https?://%s/.*' % Site.objects.get_current()
# local_referer_only = referer_matches_re(regex)
local_referer_only.__doc__ = (
"""
Decorator for views that checks that if the request's HTTP_REFERER matches
the current site. If not, a PermissionDenied exception is raised.
"""
)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
One minor enhancement:
This will allow for multiple netloc's which can be easier than a regexp which may end up looking like '(netloc1|netloc2)' otherwise.
#
Thanks, derivin... I've updated the snippet accordingly.
#
Hm - I'm not sure I get this.. HTTP_REFERER is set by the client, so is very easy to fake. Are you trying to provide security, or am I missing the point, and this has some kind of different use case? Preventing hotlinking or something?
Maybe put an example in the description?
#
The main idea is to prevent hotlinking. HTTP_REFERER is set by the client, but that doesn't mean it is easy to fake. An attacker could easily forge the headers sent by the client he is using. But how does he write an offensive website? How does he force all the clients who are surfing that site to send fake headers? The majority of these clients will be well known browsers designed not to allow such thing. This is definitely not easy. That is why checking HTTP_REFERER is an effective way to prevent hotlinking and is also a security measure.
#
Please login first before commenting.