Luke Plant committed 5ec3424

[1.2.X] Added explanatory note on CSRF failure page for the case of a 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.

Comments (0)

Files changed (2)


     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.
     request.META["CSRF_COOKIE_USED"] = True
     return request.META.get("CSRF_COOKIE", None)
 class CsrfViewMiddleware(object):
     Middleware that requires a present and correct csrfmiddlewaretoken
                 # 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
                     # 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)
                 csrf_token = request.META["CSRF_COOKIE"]
             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)
-                    return reject("CSRF token missing or incorrect.")
+                    return reject(REASON_BAD_TOKEN)
         return accept()
         response.csrf_processing_done = True
         return response
 class CsrfResponseMiddleware(object):
                 del response['ETag']
         return response
 class CsrfMiddleware(object):
     Django middleware that adds protection against Cross Site
     def process_view(self, request, callback, callback_args, callback_kwargs):
         return self.view_middleware.process_view(request, callback, callback_args,


     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; }
 <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 %}
 {% if DEBUG %}
 <div id="info">
     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')