Skip to content

Commit

Permalink
[1.2.X] Added explanatory note on CSRF failure page for the case of a…
Browse files Browse the repository at this point in the history
… missing Referer header.

  
This is intended to help power users who have disabled Referer headers, or
installed add-ons which have done so, and to help web site administrators
with debugging, since this problem will be browser specific and not a
programming error.

Backport of [13680] from trunk. Technically this is a (tiny) new feature,
but it has been backported because it might give significant help with
debugging rare problems with Django 1.2's new CSRF protection.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@13682 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
spookylukey committed Sep 3, 2010
1 parent 7a601b3 commit 94047d7
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 8 deletions.
24 changes: 18 additions & 6 deletions django/middleware/csrf.py
Expand Up @@ -27,19 +27,29 @@
randrange = random.randrange
_MAX_CSRF_KEY = 18446744073709551616L # 2 << 63

REASON_NO_REFERER = "Referer checking failed - no Referer."
REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
REASON_NO_COOKIE = "No CSRF or session cookie."
REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
REASON_BAD_TOKEN = "CSRF token missing or incorrect."


def _get_failure_view():
"""
Returns the view to be used for CSRF rejections
"""
return get_callable(settings.CSRF_FAILURE_VIEW)


def _get_new_csrf_key():
return md5_constructor("%s%s"
% (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()


def _make_legacy_session_token(session_id):
return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()


def get_token(request):
"""
Returns the the CSRF token required for a POST form.
Expand All @@ -52,6 +62,7 @@ def get_token(request):
request.META["CSRF_COOKIE_USED"] = True
return request.META.get("CSRF_COOKIE", None)


class CsrfViewMiddleware(object):
"""
Middleware that requires a present and correct csrfmiddlewaretoken
Expand Down Expand Up @@ -129,13 +140,13 @@ def accept():
# Strict referer checking for HTTPS
referer = request.META.get('HTTP_REFERER')
if referer is None:
return reject("Referer checking failed - no Referer.")
return reject(REASON_NO_REFERER)

# The following check ensures that the referer is HTTPS,
# the domains match and the ports match. This might be too strict.
good_referer = 'https://%s/' % request.get_host()
if not referer.startswith(good_referer):
return reject("Referer checking failed - %s does not match %s." %
return reject(REASON_BAD_REFERER %
(referer, good_referer))

# If the user didn't already have a CSRF cookie, then fall back to
Expand All @@ -150,7 +161,7 @@ def accept():
# No CSRF cookie and no session cookie. For POST requests,
# we insist on a CSRF cookie, and in this way we can avoid
# all CSRF attacks, including login CSRF.
return reject("No CSRF or session cookie.")
return reject(REASON_NO_COOKIE)
else:
csrf_token = request.META["CSRF_COOKIE"]

Expand All @@ -159,9 +170,9 @@ def accept():
if request_csrf_token != csrf_token:
if cookie_is_new:
# probably a problem setting the CSRF cookie
return reject("CSRF cookie not set.")
return reject(REASON_NO_CSRF_COOKIE)
else:
return reject("CSRF token missing or incorrect.")
return reject(REASON_BAD_TOKEN)

return accept()

Expand All @@ -187,6 +198,7 @@ def process_response(self, request, response):
response.csrf_processing_done = True
return response


class CsrfResponseMiddleware(object):
"""
DEPRECATED
Expand Down Expand Up @@ -237,6 +249,7 @@ def add_csrf_field(match):
del response['ETag']
return response


class CsrfMiddleware(object):
"""
Django middleware that adds protection against Cross Site
Expand Down Expand Up @@ -264,4 +277,3 @@ def process_response(self, request, resp):
def process_view(self, request, callback, callback_args, callback_kwargs):
return self.view_middleware.process_view(request, callback, callback_args,
callback_kwargs)

17 changes: 15 additions & 2 deletions django/views/csrf.py
Expand Up @@ -23,7 +23,7 @@
h1 span { font-size:60%; color:#666; font-weight:normal; }
#info { background:#f6f6f6; }
#info ul { margin: 0.5em 4em; }
#info p { padding-top:10px; }
#info p, #summary p { padding-top:10px; }
#summary { background: #ffc; }
#explanation { background:#eee; border-bottom: 0px none; }
</style>
Expand All @@ -32,6 +32,16 @@
<div id="summary">
<h1>Forbidden <span>(403)</span></h1>
<p>CSRF verification failed. Request aborted.</p>
{% if no_referer %}
<p>You are seeing this message because this HTTPS site requires a 'Referer
header' to be sent by your web browser, but none was sent. This header is
required for security reasons, to ensure that your browser is not being
hijacked by third parties.</p>
<p>If you have configured your browser to disable 'Referer' headers, please
re-enable them, at least for this site, or for HTTPS connections, or for
'same-origin' requests.</p>
{% endif %}
</div>
{% if DEBUG %}
<div id="info">
Expand Down Expand Up @@ -83,7 +93,10 @@ def csrf_failure(request, reason=""):
"""
Default view used when request fails CSRF protection
"""
from django.middleware.csrf import REASON_NO_REFERER
t = Template(CSRF_FAILRE_TEMPLATE)
c = Context({'DEBUG': settings.DEBUG,
'reason': reason})
'reason': reason,
'no_referer': reason == REASON_NO_REFERER
})
return HttpResponseForbidden(t.render(c), mimetype='text/html')

0 comments on commit 94047d7

Please sign in to comment.