Login

Referer-checking view decorators

Author:
robbie
Posted:
April 15, 2007
Language:
Python
Version:
.96
Score:
5 (after 7 ratings)

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

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 8 months ago

Comments

derivin (on April 15, 2007):

One minor enhancement:

def referer_matches_hostname(*netlocs):
...
        if referer_netloc in netlocs:

This will allow for multiple netloc's which can be easier than a regexp which may end up looking like '(netloc1|netloc2)' otherwise.

#

robbie (on April 15, 2007):

Thanks, derivin... I've updated the snippet accordingly.

#

dan90 (on March 18, 2009):

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?

#

rafa (on July 25, 2009):

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.