| 1 | from django.http import HttpResponseForbidden |
| 2 | from django.template import Context, Template |
| 3 | from django.conf import settings |
| 4 | |
| 5 | # We include the template inline since we need to be able to reliably display |
| 6 | # this error message, especially for the sake of developers, and there isn't any |
| 7 | # other way of making it available independent of what is in the settings file. |
| 8 | |
| 9 | HOSTMATCH_FAILURE_TEMPLATE = """ |
| 10 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
| 11 | <html lang="en"> |
| 12 | <head> |
| 13 | <meta http-equiv="content-type" content="text/html; charset=utf-8"> |
| 14 | <meta name="robots" content="NONE,NOARCHIVE"> |
| 15 | <title>403 Forbidden</title> |
| 16 | <style type="text/css"> |
| 17 | html * { padding:0; margin:0; } |
| 18 | body * { padding:10px 20px; } |
| 19 | body * * { padding:0; } |
| 20 | body { font:small sans-serif; background:#eee; } |
| 21 | body>div { border-bottom:1px solid #ddd; } |
| 22 | h1 { font-weight:normal; margin-bottom:.4em; } |
| 23 | h1 span { font-size:60%; color:#666; font-weight:normal; } |
| 24 | #info { background:#f6f6f6; } |
| 25 | #info ul { margin: 0.5em 4em; } |
| 26 | #info p, #summary p { padding-top:10px; } |
| 27 | #summary { background: #ffc; } |
| 28 | #explanation { background:#eee; border-bottom: 0px none; } |
| 29 | </style> |
| 30 | </head> |
| 31 | <body> |
| 32 | <div id="summary"> |
| 33 | <h1>Forbidden <span>(403)</span></h1> |
| 34 | <p>Hostname matching failed. Request aborted.</p> |
| 35 | |
| 36 | <p>The <kbd>Host</kbd> header your browser supplied did not match the hostnames that this site is configured to serve.</p> |
| 37 | |
| 38 | </div> |
| 39 | {% if DEBUG %} |
| 40 | <div id="info"> |
| 41 | <h2>Help</h2> |
| 42 | {% if reason %} |
| 43 | <p>Reason given for failure:</p> |
| 44 | <pre> |
| 45 | {{ reason }} |
| 46 | </pre> |
| 47 | {% endif %} |
| 48 | |
| 49 | <p>In general, this can occur when there is a genuine DNS rebinding attack, or when |
| 50 | <a |
| 51 | href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-csrf'>Django's |
| 52 | host matching</a> has not been used correctly. Make sure you have |
| 53 | appropriately configured <code>settings.HOSTMATCH_ALLOWED_HOSTNAMES</code>.</p> |
| 54 | |
| 55 | <p>You're seeing the help section of this page because you have <code>DEBUG = |
| 56 | True</code> in your Django settings file. Change that to <code>False</code>, |
| 57 | and only the initial error message will be displayed. </p> |
| 58 | |
| 59 | <p>You can customize this page using the HOSTMATCH_FAILURE_VIEW setting.</p> |
| 60 | </div> |
| 61 | {% else %} |
| 62 | <div id="explanation"> |
| 63 | <p><small>More information is available with DEBUG=True.</small></p> |
| 64 | </div> |
| 65 | {% endif %} |
| 66 | </body> |
| 67 | </html> |
| 68 | """ |
| 69 | |
| 70 | def hostmatch_failure(request, reason=""): |
| 71 | """ |
| 72 | Default view used when request fails hostmatch protection |
| 73 | """ |
| 74 | from django.middleware.csrf import REASON_NO_REFERER |
| 75 | t = Template(HOSTMATCH_FAILURE_TEMPLATE) |
| 76 | c = Context({'DEBUG': settings.DEBUG, |
| 77 | 'reason': reason, |
| 78 | }) |
| 79 | return HttpResponseForbidden(t.render(c), mimetype='text/html') |