Referer-checking view decorators

 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. SignedForm: CSRF-protect forms with a hidden token field by exogen 4 years, 7 months ago
  2. RequestFactory: Easily create mock request objects, for use in testing by simon 4 years, 9 months ago
  3. superuser only decorator by jedie 3 years, 11 months ago
  4. Client-side Django-style date & time string formatting by robbie 6 years, 2 months ago
  5. view by view basic authentication decorator by Scanner 6 years 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.

#

(Forgotten your password?)