Ticket #9977: csrf_template_tag_r11587_1.diff

File csrf_template_tag_r11587_1.diff, 90.4 KB (added by Luke Plant, 15 years ago)

lots of updates, including moving functionality to builtin.

  • AUTHORS

    diff -r 27807883e23f AUTHORS
    a b  
    470470    Gasper Zejn <zejn@kiberpipa.org>
    471471    Jarek Zgoda <jarek.zgoda@gmail.com>
    472472    Cheng Zhang
     473    Glenn
     474    bthomas
    473475
    474476A big THANK YOU goes to:
    475477
  • django/conf/global_settings.py

    diff -r 27807883e23f django/conf/global_settings.py
    a b  
    300300MIDDLEWARE_CLASSES = (
    301301    'django.middleware.common.CommonMiddleware',
    302302    'django.contrib.sessions.middleware.SessionMiddleware',
     303    'django.contrib.csrf.middleware.CsrfViewMiddleware',
    303304    'django.contrib.auth.middleware.AuthenticationMiddleware',
    304305#     'django.middleware.http.ConditionalGetMiddleware',
    305306#     'django.middleware.gzip.GZipMiddleware',
     
    374375# The number of days a password reset link is valid for
    375376PASSWORD_RESET_TIMEOUT_DAYS = 3
    376377
     378########
     379# CSRF #
     380########
     381
     382# Dotted path to callable to be used as view when a request is
     383# rejected by the CSRF middleware.
     384CSRF_FAILURE_VIEW = 'django.contrib.csrf.views.csrf_failure'
     385
     386# Name and domain for CSRF cookie.
     387CSRF_COOKIE_NAME = 'csrftoken'
     388CSRF_COOKIE_DOMAIN = None
     389
    377390###########
    378391# TESTING #
    379392###########
  • django/conf/project_template/settings.py

    diff -r 27807883e23f django/conf/project_template/settings.py
    a b  
    6060MIDDLEWARE_CLASSES = (
    6161    'django.middleware.common.CommonMiddleware',
    6262    'django.contrib.sessions.middleware.SessionMiddleware',
     63    'django.contrib.csrf.middleware.CsrfViewMiddleware',
    6364    'django.contrib.auth.middleware.AuthenticationMiddleware',
    6465)
    6566
  • django/contrib/admin/options.py

    diff -r 27807883e23f django/contrib/admin/options.py
    a b  
    66from django.contrib.admin import widgets
    77from django.contrib.admin import helpers
    88from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
     9from django.contrib.csrf.decorators import csrf_protect
    910from django.core.exceptions import PermissionDenied
    1011from django.db import models, transaction
    1112from django.db.models.fields import BLANK_CHOICE_DASH
     
    782783        }
    783784        context.update(extra_context or {})
    784785        return self.render_change_form(request, context, form_url=form_url, add=True)
    785     add_view = transaction.commit_on_success(add_view)
     786    add_view = csrf_protect(transaction.commit_on_success(add_view))
    786787
    787788    def change_view(self, request, object_id, extra_context=None):
    788789        "The 'change' admin view for this model."
     
    871872        }
    872873        context.update(extra_context or {})
    873874        return self.render_change_form(request, context, change=True, obj=obj)
    874     change_view = transaction.commit_on_success(change_view)
     875    change_view = csrf_protect(transaction.commit_on_success(change_view))
    875876
    876877    def changelist_view(self, request, extra_context=None):
    877878        "The 'change list' admin view for this model."
     
    984985            'admin/%s/change_list.html' % app_label,
    985986            'admin/change_list.html'
    986987        ], context, context_instance=context_instance)
     988    changelist_view = csrf_protect(changelist_view)
    987989
    988990    def delete_view(self, request, object_id, extra_context=None):
    989991        "The 'delete' admin view for this model."
     
    10401042            "admin/%s/delete_confirmation.html" % app_label,
    10411043            "admin/delete_confirmation.html"
    10421044        ], context, context_instance=context_instance)
     1045    delete_view = csrf_protect(delete_view)
    10431046
    10441047    def history_view(self, request, object_id, extra_context=None):
    10451048        "The 'history' admin view for this model."
  • django/contrib/admin/sites.py

    diff -r 27807883e23f django/contrib/admin/sites.py
    a b  
    33from django.contrib.admin import ModelAdmin
    44from django.contrib.admin import actions
    55from django.contrib.auth import authenticate, login
     6from django.contrib.csrf.middleware import csrf_response_exempt
     7from django.contrib.csrf.decorators import csrf_protect
    68from django.db.models.base import ModelBase
    79from django.core.exceptions import ImproperlyConfigured
    810from django.core.urlresolvers import reverse
     
    186188            return view(request, *args, **kwargs)
    187189        if not cacheable:
    188190            inner = never_cache(inner)
     191        # We add csrf_protect here so this function can be used as a utility
     192        # function for any view, without having to repeat 'csrf_protect'.
     193        inner = csrf_response_exempt(csrf_protect(inner))
    189194        return update_wrapper(inner, view)
    190195
    191196    def get_urls(self):
  • django/contrib/admin/templates/admin/auth/user/change_password.html

    diff -r 27807883e23f django/contrib/admin/templates/admin/auth/user/change_password.html
    a b  
    1515</div>
    1616{% endif %}{% endblock %}
    1717{% block content %}<div id="content-main">
    18 <form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
     18<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
    1919<div>
    2020{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
    2121{% if form.errors %}
  • django/contrib/admin/templates/admin/change_form.html

    diff -r 27807883e23f django/contrib/admin/templates/admin/change_form.html
    a b  
    2929  </ul>
    3030{% endif %}{% endif %}
    3131{% endblock %}
    32 <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
     32<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
    3333<div>
    3434{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
    3535{% if save_on_top %}{% submit_row %}{% endif %}
  • django/contrib/admin/templates/admin/change_list.html

    diff -r 27807883e23f django/contrib/admin/templates/admin/change_list.html
    a b  
    6868        {% endif %}
    6969      {% endblock %}
    7070     
    71       <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>
     71      <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>{% csrf_token %}
    7272      {% if cl.formset %}
    7373        {{ cl.formset.management_form }}
    7474      {% endif %}
  • django/contrib/admin/templates/admin/delete_confirmation.html

    diff -r 27807883e23f django/contrib/admin/templates/admin/delete_confirmation.html
    a b  
    2222{% else %}
    2323    <p>{% blocktrans with object as escaped_object %}Are you sure you want to delete the {{ object_name }} "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktrans %}</p>
    2424    <ul>{{ deleted_objects|unordered_list }}</ul>
    25     <form action="" method="post">
     25    <form action="" method="post">{% csrf_token %}
    2626    <div>
    2727    <input type="hidden" name="post" value="yes" />
    2828    <input type="submit" value="{% trans "Yes, I'm sure" %}" />
  • django/contrib/admin/templates/admin/delete_selected_confirmation.html

    diff -r 27807883e23f django/contrib/admin/templates/admin/delete_selected_confirmation.html
    a b  
    2323    {% for deleteable_object in deletable_objects %}
    2424        <ul>{{ deleteable_object|unordered_list }}</ul>
    2525    {% endfor %}
    26     <form action="" method="post">
     26    <form action="" method="post">{% csrf_token %}
    2727    <div>
    2828    {% for obj in queryset %}
    2929    <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk }}" />
  • django/contrib/admin/templates/admin/login.html

    diff -r 27807883e23f django/contrib/admin/templates/admin/login.html
    a b  
    1414<p class="errornote">{{ error_message }}</p>
    1515{% endif %}
    1616<div id="content-main">
    17 <form action="{{ app_path }}" method="post" id="login-form">
     17<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
    1818  <div class="form-row">
    1919    <label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
    2020  </div>
  • django/contrib/admin/templates/admin/template_validator.html

    diff -r 27807883e23f django/contrib/admin/templates/admin/template_validator.html
    a b  
    44
    55<div id="content-main">
    66
    7 <form action="" method="post">
     7<form action="" method="post">{% csrf_token %}
    88
    99{% if form.errors %}
    1010<p class="errornote">Your template had {{ form.errors|length }} error{{ form.errors|pluralize }}:</p>
  • django/contrib/admin/templates/registration/password_change_form.html

    diff -r 27807883e23f django/contrib/admin/templates/registration/password_change_form.html
    a b  
    1111
    1212<p>{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}</p>
    1313
    14 <form action="" method="post">
     14<form action="" method="post">{% csrf_token %}
    1515
    1616{{ form.old_password.errors }}
    1717<p class="aligned wide"><label for="id_old_password">{% trans 'Old password:' %}</label>{{ form.old_password }}</p>
  • django/contrib/admin/templates/registration/password_reset_confirm.html

    diff -r 27807883e23f django/contrib/admin/templates/registration/password_reset_confirm.html
    a b  
    1313
    1414<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
    1515
    16 <form action="" method="post">
     16<form action="" method="post">{% csrf_token %}
    1717{{ form.new_password1.errors }}
    1818<p class="aligned wide"><label for="id_new_password1">{% trans 'New password:' %}</label>{{ form.new_password1 }}</p>
    1919{{ form.new_password2.errors }}
  • django/contrib/admin/templates/registration/password_reset_form.html

    diff -r 27807883e23f django/contrib/admin/templates/registration/password_reset_form.html
    a b  
    1111
    1212<p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." %}</p>
    1313
    14 <form action="" method="post">
     14<form action="" method="post">{% csrf_token %}
    1515{{ form.email.errors }}
    1616<p><label for="id_email">{% trans 'E-mail address:' %}</label> {{ form.email }} <input type="submit" value="{% trans 'Reset my password' %}" /></p>
    1717</form>
  • django/contrib/auth/views.py

    diff -r 27807883e23f django/contrib/auth/views.py
    a b  
    44from django.contrib.auth.forms import AuthenticationForm
    55from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm, PasswordChangeForm
    66from django.contrib.auth.tokens import default_token_generator
     7from django.contrib.csrf.decorators import csrf_protect
    78from django.core.urlresolvers import reverse
    89from django.shortcuts import render_to_response, get_object_or_404
    910from django.contrib.sites.models import Site, RequestSite
     
    4142        'site': current_site,
    4243        'site_name': current_site.name,
    4344    }, context_instance=RequestContext(request))
    44 login = never_cache(login)
     45login = csrf_protect(never_cache(login))
    4546
    4647def logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name=REDIRECT_FIELD_NAME):
    4748    "Logs out the user and displays 'You are logged out' message."
     
    103104    return render_to_response(template_name, {
    104105        'form': form,
    105106    }, context_instance=RequestContext(request))
     107password_reset = csrf_protect(password_reset)
    106108
    107109def password_reset_done(request, template_name='registration/password_reset_done.html'):
    108110    return render_to_response(template_name, context_instance=RequestContext(request))
     
    139141        form = None
    140142    context_instance['form'] = form   
    141143    return render_to_response(template_name, context_instance=context_instance)
     144# Doesn't need csrf_protect since no-one can guess the URL
    142145
    143146def password_reset_complete(request, template_name='registration/password_reset_complete.html'):
    144147    return render_to_response(template_name, context_instance=RequestContext(request,
     
    158161    return render_to_response(template_name, {
    159162        'form': form,
    160163    }, context_instance=RequestContext(request))
    161 password_change = login_required(password_change)
     164password_change = csrf_protect(login_required(password_change))
    162165
    163166def password_change_done(request, template_name='registration/password_change_done.html'):
    164167    return render_to_response(template_name, context_instance=RequestContext(request))
  • django/contrib/comments/templates/comments/approve.html

    diff -r 27807883e23f django/contrib/comments/templates/comments/approve.html
    a b  
    66{% block content %}
    77  <h1>{% trans "Really make this comment public?" %}</h1>
    88  <blockquote>{{ comment|linebreaks }}</blockquote>
    9   <form action="." method="post">
     9  <form action="." method="post">{% csrf_token %}
    1010    {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %}
    1111    <p class="submit">
    1212      <input type="submit" name="submit" value="{% trans "Approve" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a>
  • django/contrib/comments/templates/comments/delete.html

    diff -r 27807883e23f django/contrib/comments/templates/comments/delete.html
    a b  
    66{% block content %}
    77<h1>{% trans "Really remove this comment?" %}</h1>
    88  <blockquote>{{ comment|linebreaks }}</blockquote>
    9   <form action="." method="post">
     9  <form action="." method="post">{% csrf_token %}
    1010    {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %}
    1111    <p class="submit">
    1212    <input type="submit" name="submit" value="{% trans "Remove" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a>
  • django/contrib/comments/templates/comments/flag.html

    diff -r 27807883e23f django/contrib/comments/templates/comments/flag.html
    a b  
    66{% block content %}
    77<h1>{% trans "Really flag this comment?" %}</h1>
    88  <blockquote>{{ comment|linebreaks }}</blockquote>
    9   <form action="." method="post">
     9  <form action="." method="post">{% csrf_token %}
    1010    {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %}
    1111    <p class="submit">
    1212    <input type="submit" name="submit" value="{% trans "Flag" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a>
  • django/contrib/comments/templates/comments/form.html

    diff -r 27807883e23f django/contrib/comments/templates/comments/form.html
    a b  
    11{% load comments i18n %}
    2 <form action="{% comment_form_target %}" method="post">
     2<form action="{% comment_form_target %}" method="post">{% csrf_token %}
    33  {% if next %}<input type="hidden" name="next" value="{{ next }}" />{% endif %}
    44  {% for field in form %}
    55    {% if field.is_hidden %}
  • django/contrib/comments/templates/comments/moderation_queue.html

    diff -r 27807883e23f django/contrib/comments/templates/comments/moderation_queue.html
    a b  
    4444      {% for comment in comments %}
    4545        <tr class="{% cycle 'row1' 'row2' %}">
    4646          <td class="actions">
    47             <form action="{% url comments-approve comment.pk %}" method="post">
     47            <form action="{% url comments-approve comment.pk %}" method="post">{% csrf_token %}
    4848              <input type="hidden" name="next" value="{% url comments-moderation-queue %}" />
    4949              <input class="approve submit" type="submit" name="submit" value="{% trans "Approve" %}" />
    5050            </form>
    51             <form action="{% url comments-delete comment.pk %}" method="post">
     51            <form action="{% url comments-delete comment.pk %}" method="post">{% csrf_token %}
    5252              <input type="hidden" name="next" value="{% url comments-moderation-queue %}" />
    5353              <input class="remove submit" type="submit" name="submit" value="{% trans "Remove" %}" />
    5454            </form>
  • django/contrib/comments/templates/comments/preview.html

    diff -r 27807883e23f django/contrib/comments/templates/comments/preview.html
    a b  
    55
    66{% block content %}
    77  {% load comments %}
    8   <form action="{% comment_form_target %}" method="post">
     8  <form action="{% comment_form_target %}" method="post">{% csrf_token %}
    99    {% if next %}<input type="hidden" name="next" value="{{ next }}" />{% endif %}
    1010    {% if form.errors %}
    1111    <h1>{% blocktrans count form.errors|length as counter %}Please correct the error below{% plural %}Please correct the errors below{% endblocktrans %}</h1>
  • django/contrib/comments/views/comments.py

    diff -r 27807883e23f django/contrib/comments/views/comments.py
    a b  
    1010from django.views.decorators.http import require_POST
    1111from django.contrib import comments
    1212from django.contrib.comments import signals
     13from django.contrib.csrf.decorators import csrf_protect
    1314
    1415class CommentPostBadRequest(http.HttpResponseBadRequest):
    1516    """
     
    116117
    117118    return next_redirect(data, next, comment_done, c=comment._get_pk_val())
    118119
    119 post_comment = require_POST(post_comment)
     120post_comment = csrf_protect(require_POST(post_comment))
    120121
    121122comment_done = confirmation_view(
    122123    template = "comments/posted.html",
  • django/contrib/comments/views/moderation.py

    diff -r 27807883e23f django/contrib/comments/views/moderation.py
    a b  
    77from django.http import Http404
    88from django.contrib import comments
    99from django.contrib.comments import signals
     10from django.contrib.csrf.decorators import csrf_protect
    1011
    1112#@login_required
    1213def flag(request, comment_id, next=None):
     
    4243            {'comment': comment, "next": next},
    4344            template.RequestContext(request)
    4445        )
    45 flag = login_required(flag)
     46flag = csrf_protect(login_required(flag))
    4647
    4748#@permission_required("comments.delete_comment")
    4849def delete(request, comment_id, next=None):
     
    8283            {'comment': comment, "next": next},
    8384            template.RequestContext(request)
    8485        )
    85 delete = permission_required("comments.can_moderate")(delete)
     86delete = csrf_protect(permission_required("comments.can_moderate")(delete))
    8687
    8788#@permission_required("comments.can_moderate")
    8889def approve(request, comment_id, next=None):
     
    126127            template.RequestContext(request)
    127128        )
    128129
    129 approve = permission_required("comments.can_moderate")(approve)
     130approve = csrf_protect(permission_required("comments.can_moderate")(approve))
    130131
    131132
    132133#@permission_required("comments.can_moderate")
  • new file django/contrib/csrf/context_processors.py

    diff -r 27807883e23f django/contrib/csrf/context_processors.py
    - +  
     1from django.contrib.csrf.middleware import get_token
     2from django.utils.functional import lazy
     3
     4def csrf(request):
     5    """
     6    Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if
     7    it has not been provided by either a view decorator or the middleware
     8    """
     9    def _get_val():
     10        token = get_token(request)
     11        if token is None:
     12            # In order to be able to provide debugging info in the
     13            # case of misconfiguration, we use a sentinel value
     14            # instead of returning an empty dict.
     15            return 'NOTPROVIDED'
     16        else:
     17            return token
     18    _get_val = lazy(_get_val, str)
     19
     20    return {'csrf_token': _get_val() }
  • new file django/contrib/csrf/decorators.py

    diff -r 27807883e23f django/contrib/csrf/decorators.py
    - +  
     1from django.contrib.csrf.middleware import CsrfViewMiddleware
     2from django.utils.decorators import decorator_from_middleware
     3
     4csrf_protect = decorator_from_middleware(CsrfViewMiddleware)
     5csrf_protect.__name__ = "csrf_protect"
     6csrf_protect.__doc__ = """
     7This decorator adds CSRF protection in exactly the same way as
     8CsrfViewMiddleware, but it can be used on a per view basis.  Using both, or
     9using the decorator multiple times, is harmless and efficient.
     10"""
  • django/contrib/csrf/middleware.py

    diff -r 27807883e23f django/contrib/csrf/middleware.py
    a b  
    55against request forgeries from other sites.
    66"""
    77
     8import itertools
    89import re
    9 import itertools
     10import random
    1011try:
    1112    from functools import wraps
    1213except ImportError:
    1314    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
    1415
    1516from django.conf import settings
    16 from django.http import HttpResponseForbidden
     17from django.core.urlresolvers import get_callable
     18from django.utils.cache import patch_vary_headers
    1719from django.utils.hashcompat import md5_constructor
    1820from django.utils.safestring import mark_safe
    1921
    20 _ERROR_MSG = mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>')
    21 
    2222_POST_FORM_RE = \
    2323    re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
    2424
    2525_HTML_TYPES = ('text/html', 'application/xhtml+xml')
    2626
    27 def _make_token(session_id):
     27# Use the system (hardware-based) random number generator if it exists.
     28if hasattr(random, 'SystemRandom'):
     29    randrange = random.SystemRandom().randrange
     30else:
     31    randrange = random.randrange
     32_MAX_CSRF_KEY = 18446744073709551616L     # 2 << 63
     33
     34def _get_failure_view():
     35    """
     36    Returns the view to be used for CSRF rejections
     37    """
     38    return get_callable(settings.CSRF_FAILURE_VIEW)
     39
     40def _get_new_csrf_key():
     41    return md5_constructor("%s%s"
     42                % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
     43
     44def _make_legacy_session_token(session_id):
    2845    return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
    2946
     47def get_token(request):
     48    """
     49    Returns the the CSRF token required for a POST form.
     50    """
     51    request.META["CSRF_COOKIE_USED"] = True
     52    return request.META.get("CSRF_COOKIE", None)
     53
    3054class CsrfViewMiddleware(object):
    3155    """
    3256    Middleware that requires a present and correct csrfmiddlewaretoken
    33     for POST requests that have an active session.
     57    for POST requests that have a CSRF cookie, and sets an outgoing
     58    CSRF cookie.
     59
     60    This middleware should be used in conjunction with the csrf_token template
     61    tag.
    3462    """
    3563    def process_view(self, request, callback, callback_args, callback_kwargs):
     64        if getattr(callback, 'csrf_exempt', False):
     65            return None
     66
     67        if getattr(request, 'csrf_processing_done', False):
     68            return None
     69
     70        reject = lambda s: _get_failure_view()(request, reason=s)
     71        def accept():
     72            # Avoid checking the request twice by adding a custom attribute to
     73            # request.  This will be relevant when both decorator and middleware
     74            # are used.
     75            request.csrf_processing_done = True
     76            return None
     77
     78        # If the user doesn't have a CSRF cookie, generate one and store it in the
     79        # request, so it's available to the view.  We'll store it in a cookie when
     80        # we reach the response.
     81        try:
     82            request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME]
     83            cookie_is_new = False
     84        except KeyError:
     85            # No cookie, so create one.
     86            request.META["CSRF_COOKIE"] = _get_new_csrf_key()
     87            cookie_is_new = True
     88
    3689        if request.method == 'POST':
    37             if getattr(callback, 'csrf_exempt', False):
    38                 return None
     90            if getattr(request, 'dont_enforce_csrf_checks', False):
     91                # Mechanism to turn off CSRF checks for test suite.  It comes after
     92                # the creation of CSRF cookies, so that everything else continues to
     93                # work exactly the same (e.g. cookies are sent etc), but before the
     94                # any branches that call reject()
     95                return accept()
    3996
    4097            if request.is_ajax():
    41                 return None
     98                # .is_ajax() is based on the presence of X-Requested-With.  In
     99                # the context of a browser, this can only be sent if using
     100                # XmlHttpRequest.  Browsers implement careful policies for
     101                # XmlHttpRequest:
     102                #
     103                #  * Normally, only same-domain requests are allowed.
     104                #
     105                #  * Some browsers (e.g. Firefox 3.5 and later) relax this
     106                #    carefully:
     107                #
     108                #    * if it is a 'simple' GET or POST request (which can
     109                #      include no custom headers), it is allowed to be cross
     110                #      domain.  These requests will not be recognized as AJAX.
     111                #
     112                #    * if a 'preflight' check with the server confirms that the
     113                #      server is expecting and allows the request, cross domain
     114                #      requests even with custom headers are allowed. These
     115                #      requests will be recognized as AJAX, but can only get
     116                #      through when the developer has specifically opted in to
     117                #      allowing the cross-domain POST request.
     118                #
     119                # So in all cases, it is safe to allow these requests through.
     120                return accept()
    42121
    43             try:
    44                 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
    45             except KeyError:
    46                 # No session, no check required
    47                 return None
     122            if request.is_secure():
     123                # Strict referer checking for HTTPS
     124                referer = request.META.get('HTTP_REFERER')
     125                if referer is None:
     126                    return reject("Referer checking failed - no Referer.")
    48127
    49             csrf_token = _make_token(session_id)
     128                # The following check ensures that the referer is HTTPS,
     129                # the domains match and the ports match.  This might be too strict.
     130                good_referer = 'https://%s/' % request.get_host()
     131                if not referer.startswith(good_referer):
     132                    return reject("Referer checking failed - %s does not match %s." %
     133                                  (referer, good_referer))
     134
     135            # If the user didn't already have a CSRF key, then accept the
     136            # session key for the middleware token, so CSRF protection isn't lost
     137            # for the period between upgrading to CSRF cookes to the first time
     138            # each user comes back to the site to receive one.
     139            if cookie_is_new:
     140                try:
     141                    session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
     142                    csrf_token = _make_legacy_session_token(session_id)
     143                except KeyError:
     144                    # No CSRF cookie and no session cookie. For POST requests,
     145                    # we insist on a CSRF cookie, and in this way we can avoid
     146                    # all CSRF attacks, including login CSRF.
     147                    return reject("No CSRF cookie.")
     148            else:
     149                csrf_token = request.META["CSRF_COOKIE"]
     150
    50151            # check incoming token
    51             try:
    52                 request_csrf_token = request.POST['csrfmiddlewaretoken']
    53             except KeyError:
    54                 return HttpResponseForbidden(_ERROR_MSG)
     152            request_csrf_token = request.POST.get('csrfmiddlewaretoken', None)
     153            if request_csrf_token != csrf_token:
     154                return reject("CSRF token missing or incorrect.")
    55155
    56             if request_csrf_token != csrf_token:
    57                 return HttpResponseForbidden(_ERROR_MSG)
     156        return accept()
    58157
    59         return None
     158    def process_response(self, request, response):
     159        if getattr(response, 'csrf_processing_done', False):
     160            return response
     161
     162        # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
     163        # never called, probaby because a request middleware returned a response
     164        # (for example, contrib.auth redirecting to a login page).
     165        if request.META.get("CSRF_COOKIE") is None:
     166            return response
     167
     168        if not request.META.get("CSRF_COOKIE_USED", False):
     169            return response
     170
     171        # Set the CSRF cookie even if it's already set, so we renew the expiry timer.
     172        response.set_cookie(settings.CSRF_COOKIE_NAME,
     173                request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
     174                domain=settings.CSRF_COOKIE_DOMAIN)
     175        # Content varies with the CSRF cookie, so set the Vary header.
     176        patch_vary_headers(response, ('Cookie',))
     177        response.csrf_processing_done = True
     178        return response
    60179
    61180class CsrfResponseMiddleware(object):
    62181    """
    63     Middleware that post-processes a response to add a
    64     csrfmiddlewaretoken if the response/request have an active
    65     session.
     182    DEPRECATED
     183    Middleware that post-processes a response to add a csrfmiddlewaretoken.
     184
     185    This exists for backwards compatibility and as an interim measure until
     186    applications are converted to using use the csrf_token template tag
     187    instead. It will be removed in Django 1.4.
    66188    """
     189    def __init__(self):
     190        import warnings
     191        warnings.warn(
     192            "CsrfResponseMiddleware and CsrfMiddleware are deprecated; use CsrfViewMiddleware and the template tag instead (see CSRF documentation).",
     193            PendingDeprecationWarning
     194        )
     195
    67196    def process_response(self, request, response):
    68197        if getattr(response, 'csrf_exempt', False):
    69198            return response
    70199
    71         csrf_token = None
    72         try:
    73             # This covers a corner case in which the outgoing response
    74             # both contains a form and sets a session cookie.  This
    75             # really should not be needed, since it is best if views
    76             # that create a new session (login pages) also do a
    77             # redirect, as is done by all such view functions in
    78             # Django.
    79             cookie = response.cookies[settings.SESSION_COOKIE_NAME]
    80             csrf_token = _make_token(cookie.value)
    81         except KeyError:
    82             # Normal case - look for existing session cookie
    83             try:
    84                 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
    85                 csrf_token = _make_token(session_id)
    86             except KeyError:
    87                 # no incoming or outgoing cookie
    88                 pass
    89 
    90         if csrf_token is not None and \
    91                 response['Content-Type'].split(';')[0] in _HTML_TYPES:
     200        if response['Content-Type'].split(';')[0] in _HTML_TYPES:
     201            csrf_token = get_token(request)
     202            # If csrf_token is None, we have no token for this request, which probably
     203            # means that this is a response from a request middleware.
     204            if csrf_token is None:
     205                return response
    92206
    93207            # ensure we don't add the 'id' attribute twice (HTML validity)
    94208            idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
    95                                             itertools.repeat(''))
     209                                           itertools.repeat(''))
    96210            def add_csrf_field(match):
    97211                """Returns the matched <form> tag plus the added <input> element"""
    98212                return mark_safe(match.group() + "<div style='display:none;'>" + \
     
    101215                "' /></div>")
    102216
    103217            # Modify any POST forms
    104             response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
     218            response.content, n = _POST_FORM_RE.subn(add_csrf_field, response.content)
     219            if n > 0:
     220                # Content varies with the CSRF cookie, so set the Vary header.
     221                patch_vary_headers(response, ('Cookie',))
    105222        return response
    106223
    107 class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware):
    108     """Django middleware that adds protection against Cross Site
     224class CsrfMiddleware(object):
     225    """
     226    Django middleware that adds protection against Cross Site
    109227    Request Forgeries by adding hidden form fields to POST forms and
    110228    checking requests for the correct value.
    111229
    112     In the list of middlewares, SessionMiddleware is required, and
    113     must come after this middleware.  CsrfMiddleWare must come after
    114     compression middleware.
     230    CsrfMiddleware uses two middleware, CsrfViewMiddleware and
     231    CsrfResponseMiddleware, which can be used independently.  It is recommended
     232    to use only CsrfViewMiddleware and use the csrf_token template tag in
     233    templates for inserting the token.
     234    """
     235    # We can't just inherit from CsrfViewMiddleware and CsrfResponseMiddleware
     236    # because both have process_response methods.
     237    def __init__(self):
     238        self.response_middleware = CsrfResponseMiddleware()
     239        self.view_middleware = CsrfViewMiddleware()
    115240
    116     If a session ID cookie is present, it is hashed with the
    117     SECRET_KEY setting to create an authentication token.  This token
    118     is added to all outgoing POST forms and is expected on all
    119     incoming POST requests that have a session ID cookie.
     241    def process_response(self, request, resp):
     242        # We must do the response post-processing first, because that calls
     243        # get_token(), which triggers a flag saying that the CSRF cookie needs
     244        # to be sent (done in CsrfViewMiddleware.process_response)
     245        resp2 = self.response_middleware.process_response(request, resp)
     246        return self.view_middleware.process_response(request, resp2)
    120247
    121     If you are setting cookies directly, instead of using Django's
    122     session framework, this middleware will not work.
    123 
    124     CsrfMiddleWare is composed of two middleware, CsrfViewMiddleware
    125     and CsrfResponseMiddleware which can be used independently.
    126     """
    127     pass
     248    def process_view(self, request, callback, callback_args, callback_kwargs):
     249        return self.view_middleware.process_view(request, callback, callback_args,
     250                                                 callback_kwargs)
    128251
    129252def csrf_response_exempt(view_func):
    130253    """
  • django/contrib/csrf/tests.py

    diff -r 27807883e23f django/contrib/csrf/tests.py
    a b  
    11# -*- coding: utf-8 -*-
    22
    33from django.test import TestCase
    4 from django.http import HttpRequest, HttpResponse, HttpResponseForbidden
    5 from django.contrib.csrf.middleware import CsrfMiddleware, _make_token, csrf_exempt
     4from django.http import HttpRequest, HttpResponse
     5from django.contrib.csrf.middleware import CsrfMiddleware, CsrfViewMiddleware, csrf_exempt
     6from django.contrib.csrf.context_processors import csrf
     7from django.contrib.sessions.middleware import SessionMiddleware
     8from django.utils.importlib import import_module
    69from django.conf import settings
     10from django.template import RequestContext, Template
    711
    8 
     12# Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests
    913def post_form_response():
    1014    resp = HttpResponse(content="""
    1115<html><body><form method="POST"><input type="text" /></form></body></html>
    1216""", mimetype="text/html")
    1317    return resp
    1418
    15 def test_view(request):
     19def post_form_response_non_html():
     20    resp = post_form_response()
     21    resp["Content-Type"] = "application/xml"
     22    return resp
     23
     24def post_form_view(request):
     25    """A view that returns a POST form (without a token)"""
    1626    return post_form_response()
    1727
     28# Response/views used for template tag tests
     29def _token_template():
     30    return Template("{% csrf_token %}")
     31
     32def _render_csrf_token_template(req):
     33    context = RequestContext(req, processors=[csrf])
     34    template = _token_template()
     35    return template.render(context)
     36
     37def token_view(request):
     38    """A view that uses {% csrf_token %}"""
     39    return HttpResponse(_render_csrf_token_template(request))
     40
     41def non_token_view_using_request_processor(request):
     42    """
     43    A view that doesn't use the token, but does use the csrf view processor.
     44    """
     45    context = RequestContext(request, processors=[csrf])
     46    template = Template("")
     47    return HttpResponse(template.render(context))
     48
     49class TestingHttpRequest(HttpRequest):
     50    """
     51    A version of HttpRequest that allows us to change some things
     52    more easily
     53    """
     54    def is_secure(self):
     55        return getattr(self, '_is_secure', False)
     56
    1857class CsrfMiddlewareTest(TestCase):
     58    _csrf_id = "1"
    1959
     60    # This is a valid session token for this ID and secret key.  This was generated using
     61    # the old code that we're to be backwards-compatible with.  Don't use the CSRF code
     62    # to generate this hash, or we're merely testing the code against itself and not
     63    # checking backwards-compatibility.  This is also the output of (echo -n test1 | md5sum).
     64    _session_token = "5a105e8b9d40e1329780d62ea2265d8a"
    2065    _session_id = "1"
     66    _secret_key_for_session_test= "test"
    2167
    22     def _get_GET_no_session_request(self):
    23         return HttpRequest()
     68    def _get_GET_no_csrf_cookie_request(self):
     69        return TestingHttpRequest()
    2470
    25     def _get_GET_session_request(self):
    26         req = self._get_GET_no_session_request()
     71    def _get_GET_csrf_cookie_request(self):
     72        req = TestingHttpRequest()
     73        req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id
     74        return req
     75
     76    def _get_POST_csrf_cookie_request(self):
     77        req = self._get_GET_csrf_cookie_request()
     78        req.method = "POST"
     79        return req
     80
     81    def _get_POST_no_csrf_cookie_request(self):
     82        req = self._get_GET_no_csrf_cookie_request()
     83        req.method = "POST"
     84        return req
     85
     86    def _get_POST_request_with_token(self):
     87        req = self._get_POST_csrf_cookie_request()
     88        req.POST['csrfmiddlewaretoken'] = self._csrf_id
     89        return req
     90
     91    def _get_POST_session_request_with_token(self):
     92        req = self._get_POST_no_csrf_cookie_request()
     93        req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
     94        req.POST['csrfmiddlewaretoken'] = self._session_token
     95        return req
     96
     97    def _get_POST_session_request_no_token(self):
     98        req = self._get_POST_no_csrf_cookie_request()
    2799        req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
    28100        return req
    29101
    30     def _get_POST_session_request(self):
    31         req = self._get_GET_session_request()
    32         req.method = "POST"
    33         return req
     102    def _check_token_present(self, response, csrf_id=None):
     103        self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
    34104
    35     def _get_POST_no_session_request(self):
    36         req = self._get_GET_no_session_request()
    37         req.method = "POST"
    38         return req
     105    # Check the post processing and outgoing cookie
     106    def test_process_response_no_csrf_cookie(self):
     107        """
     108        When no prior CSRF cookie exists, check that the cookie is created and a
     109        token is inserted.
     110        """
     111        req = self._get_GET_no_csrf_cookie_request()
     112        CsrfMiddleware().process_view(req, post_form_view, (), {})
    39113
    40     def _get_POST_session_request_with_token(self):
    41         req = self._get_POST_session_request()
    42         req.POST['csrfmiddlewaretoken'] = _make_token(self._session_id)
    43         return req
    44 
    45     def _get_post_form_response(self):
    46         return post_form_response()
    47 
    48     def _get_new_session_response(self):
    49         resp = self._get_post_form_response()
    50         resp.cookies[settings.SESSION_COOKIE_NAME] = self._session_id
    51         return resp
    52 
    53     def _check_token_present(self, response):
    54         self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % _make_token(self._session_id))
    55 
    56     def get_view(self):
    57         return test_view
    58 
    59     # Check the post processing
    60     def test_process_response_no_session(self):
    61         """
    62         Check the post-processor does nothing if no session active
    63         """
    64         req = self._get_GET_no_session_request()
    65         resp = self._get_post_form_response()
     114        resp = post_form_response()
    66115        resp_content = resp.content # needed because process_response modifies resp
    67116        resp2 = CsrfMiddleware().process_response(req, resp)
    68         self.assertEquals(resp_content, resp2.content)
    69117
    70     def test_process_response_existing_session(self):
     118        csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
     119        self.assertNotEqual(csrf_cookie, False)
     120        self.assertNotEqual(resp_content, resp2.content)
     121        self._check_token_present(resp2, csrf_cookie.value)
     122        # Check the Vary header got patched correctly
     123        self.assert_('Cookie' in resp2.get('Vary',''))
     124
     125    def test_process_response_no_csrf_cookie_view_only_get_token_used(self):
    71126        """
    72         Check that the token is inserted if there is an existing session
     127        When no prior CSRF cookie exists, check that the cookie is created, even
     128        if only CsrfViewMiddleware is used.
    73129        """
    74         req = self._get_GET_session_request()
    75         resp = self._get_post_form_response()
     130        # This is checking that CsrfViewMiddleware has the cookie setting
     131        # code. Most of the other tests use CsrfMiddleware.
     132        req = self._get_GET_no_csrf_cookie_request()
     133        # token_view calls get_token() indirectly
     134        CsrfViewMiddleware().process_view(req, token_view, (), {})
     135        resp = token_view(req)
     136        resp2 = CsrfViewMiddleware().process_response(req, resp)
     137
     138        csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
     139        self.assertNotEqual(csrf_cookie, False)
     140
     141    def test_process_response_get_token_not_used(self):
     142        """
     143        Check that if get_token() is not called, the view middleware does not
     144        add a cookie.
     145        """
     146        # This is important to make pages cacheable.  Pages which do call
     147        # get_token(), assuming they use the token, are not cacheable because
     148        # the token is specific to the user
     149        req = self._get_GET_no_csrf_cookie_request()
     150        # non_token_view_using_request_processor does not call get_token(), but
     151        # does use the csrf request processor.  By using this, we are testing
     152        # that the view processor is properly lazy and doesn't call get_token()
     153        # until needed.
     154        CsrfViewMiddleware().process_view(req, non_token_view_using_request_processor, (), {})
     155        resp = non_token_view_using_request_processor(req)
     156        resp2 = CsrfViewMiddleware().process_response(req, resp)
     157
     158        csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
     159        self.assertEqual(csrf_cookie, False)
     160
     161    def test_process_response_existing_csrf_cookie(self):
     162        """
     163        Check that the token is inserted when a prior CSRF cookie exists
     164        """
     165        req = self._get_GET_csrf_cookie_request()
     166        CsrfMiddleware().process_view(req, post_form_view, (), {})
     167
     168        resp = post_form_response()
    76169        resp_content = resp.content # needed because process_response modifies resp
    77170        resp2 = CsrfMiddleware().process_response(req, resp)
    78171        self.assertNotEqual(resp_content, resp2.content)
    79172        self._check_token_present(resp2)
    80173
    81     def test_process_response_new_session(self):
     174    def test_process_response_non_html(self):
    82175        """
    83         Check that the token is inserted if there is a new session being started
     176        Check the the post-processor does nothing for content-types not in _HTML_TYPES.
    84177        """
    85         req = self._get_GET_no_session_request() # no session in request
    86         resp = self._get_new_session_response() # but new session started
     178        req = self._get_GET_no_csrf_cookie_request()
     179        CsrfMiddleware().process_view(req, post_form_view, (), {})
     180        resp = post_form_response_non_html()
    87181        resp_content = resp.content # needed because process_response modifies resp
    88182        resp2 = CsrfMiddleware().process_response(req, resp)
    89         self.assertNotEqual(resp_content, resp2.content)
    90         self._check_token_present(resp2)
     183        self.assertEquals(resp_content, resp2.content)
    91184
    92185    def test_process_response_exempt_view(self):
    93186        """
    94187        Check that no post processing is done for an exempt view
    95188        """
    96         req = self._get_POST_session_request()
    97         resp = csrf_exempt(self.get_view())(req)
     189        req = self._get_POST_csrf_cookie_request()
     190        resp = csrf_exempt(post_form_view)(req)
    98191        resp_content = resp.content
    99192        resp2 = CsrfMiddleware().process_response(req, resp)
    100193        self.assertEquals(resp_content, resp2.content)
    101194
    102195    # Check the request processing
    103     def test_process_request_no_session(self):
     196    def test_process_request_no_session_no_csrf_cookie(self):
    104197        """
    105         Check that if no session is present, the middleware does nothing.
    106         to the incoming request.
     198        Check that if neither a CSRF cookie nor a session cookie are present,
     199        the middleware rejects the incoming request.  This will stop login CSRF.
    107200        """
    108         req = self._get_POST_no_session_request()
    109         req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
     201        req = self._get_POST_no_csrf_cookie_request()
     202        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
     203        self.assertEquals(403, req2.status_code)
     204
     205    def test_process_request_csrf_cookie_no_token(self):
     206        """
     207        Check that if a CSRF cookie is present but no token, the middleware
     208        rejects the incoming request.
     209        """
     210        req = self._get_POST_csrf_cookie_request()
     211        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
     212        self.assertEquals(403, req2.status_code)
     213
     214    def test_process_request_csrf_cookie_and_token(self):
     215        """
     216        Check that if both a cookie and a token is present, the middleware lets it through.
     217        """
     218        req = self._get_POST_request_with_token()
     219        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
    110220        self.assertEquals(None, req2)
    111221
    112     def test_process_request_session_no_token(self):
     222    def test_process_request_session_cookie_no_csrf_cookie_token(self):
    113223        """
    114         Check that if a session is present but no token, we get a 'forbidden'
     224        When no CSRF cookie exists, but the user has a session, check that a token
     225        using the session cookie as a legacy CSRF cookie is accepted.
    115226        """
    116         req = self._get_POST_session_request()
    117         req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
    118         self.assertEquals(HttpResponseForbidden, req2.__class__)
     227        orig_secret_key = settings.SECRET_KEY
     228        settings.SECRET_KEY = self._secret_key_for_session_test
     229        try:
     230            req = self._get_POST_session_request_with_token()
     231            req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
     232            self.assertEquals(None, req2)
     233        finally:
     234            settings.SECRET_KEY = orig_secret_key
    119235
    120     def test_process_request_session_and_token(self):
     236    def test_process_request_session_cookie_no_csrf_cookie_no_token(self):
    121237        """
    122         Check that if a session is present and a token, the middleware lets it through
     238        Check that if a session cookie is present but no token and no CSRF cookie,
     239        the request is rejected.
    123240        """
    124         req = self._get_POST_session_request_with_token()
    125         req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
    126         self.assertEquals(None, req2)
     241        req = self._get_POST_session_request_no_token()
     242        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
     243        self.assertEquals(403, req2.status_code)
    127244
    128     def test_process_request_session_no_token_exempt_view(self):
     245    def test_process_request_csrf_cookie_no_token_exempt_view(self):
    129246        """
    130         Check that if a session is present and no token, but the csrf_exempt
     247        Check that if a CSRF cookie is present and no token, but the csrf_exempt
    131248        decorator has been applied to the view, the middleware lets it through
    132249        """
    133         req = self._get_POST_session_request()
    134         req2 = CsrfMiddleware().process_view(req, csrf_exempt(self.get_view()), (), {})
     250        req = self._get_POST_csrf_cookie_request()
     251        req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {})
    135252        self.assertEquals(None, req2)
    136253
    137254    def test_ajax_exemption(self):
    138255        """
    139256        Check that AJAX requests are automatically exempted.
    140257        """
    141         req = self._get_POST_session_request()
     258        req = self._get_POST_csrf_cookie_request()
    142259        req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
    143         req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
     260        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
    144261        self.assertEquals(None, req2)
     262
     263    # Tests for the template tag method
     264    def test_token_node_no_csrf_cookie(self):
     265        """
     266        Check that CsrfTokenNode works when no CSRF cookie is set
     267        """
     268        req = self._get_GET_no_csrf_cookie_request()
     269        resp = token_view(req)
     270        self.assertEquals(u"", resp.content)
     271
     272    def test_token_node_with_csrf_cookie(self):
     273        """
     274        Check that CsrfTokenNode works when a CSRF cookie is set
     275        """
     276        req = self._get_GET_csrf_cookie_request()
     277        CsrfViewMiddleware().process_view(req, token_view, (), {})
     278        resp = token_view(req)
     279        self._check_token_present(resp)
     280
     281    def test_token_node_with_new_csrf_cookie(self):
     282        """
     283        Check that CsrfTokenNode works when a CSRF cookie is created by
     284        the middleware (when one was not already present)
     285        """
     286        req = self._get_GET_no_csrf_cookie_request()
     287        CsrfViewMiddleware().process_view(req, token_view, (), {})
     288        resp = token_view(req)
     289        resp2 = CsrfViewMiddleware().process_response(req, resp)
     290        csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME]
     291        self._check_token_present(resp, csrf_id=csrf_cookie.value)
     292
     293    def test_response_middleware_without_view_middleware(self):
     294        """
     295        Check that CsrfResponseMiddleware finishes without error if the view middleware
     296        has not been called, as is the case if a request middleware returns a response.
     297        """
     298        req = self._get_GET_no_csrf_cookie_request()
     299        resp = post_form_view(req)
     300        CsrfMiddleware().process_response(req, resp)
     301
     302    def test_https_bad_referer(self):
     303        """
     304        Test that a POST HTTPS request with a bad referer is rejected
     305        """
     306        req = self._get_POST_request_with_token()
     307        req._is_secure = True
     308        req.META['HTTP_HOST'] = 'www.example.com'
     309        req.META['HTTP_REFERER'] = 'https://www.evil.org/somepage'
     310        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
     311        self.assertNotEqual(None, req2)
     312        self.assertEquals(403, req2.status_code)
     313
     314    def test_https_good_referer(self):
     315        """
     316        Test that a POST HTTPS request with a good referer is accepted
     317        """
     318        req = self._get_POST_request_with_token()
     319        req._is_secure = True
     320        req.META['HTTP_HOST'] = 'www.example.com'
     321        req.META['HTTP_REFERER'] = 'https://www.example.com/somepage'
     322        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
     323        self.assertEquals(None, req2)
  • new file django/contrib/csrf/views.py

    diff -r 27807883e23f django/contrib/csrf/views.py
    - +  
     1from django.http import HttpResponseForbidden
     2from django.template import Context, Template
     3from django.conf import settings
     4
     5# We include the template here, since one possible error in setting up CSRF is
     6# that the developer hasn't included the CSRF app in INSTALLED_APPS, in which
     7# case we won't be able to find the template.
     8CSRF_FAILRE_TEMPLATE = """
     9<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
     10<html lang="en">
     11<head>
     12  <meta http-equiv="content-type" content="text/html; charset=utf-8">
     13  <title>403 Forbidden</title>
     14</head>
     15<body>
     16  <h1>403 Forbidden</h1>
     17  <p>CSRF verification failed. Request aborted.</p>
     18  {% if DEBUG %}
     19  <h2>Help</h2>
     20    {% if reason %}
     21    <p>Reason given for failure:</p>
     22    <pre>
     23    {{ reason }}
     24    </pre>
     25    {% endif %}
     26
     27  <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
     28  <a
     29  href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-csrf'>Django's
     30  CSRF mechanism</a> has not been used correctly.  For POST forms, you need to
     31  ensure:</p>
     32
     33  <ul>
     34    <li>The view function uses <a
     35    href='http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext'><tt>RequestContext</tt></a>
     36    for the template, instead of <tt>Context</tt>.</li>
     37
     38    <li>In the template, there is a <tt>{% templatetag openblock %} csrf_token
     39    {% templatetag closeblock %}</tt> template tag inside each POST form that
     40    targets an internal URL.</li>
     41  </ul>
     42
     43  <p>You're seeing the help section of this page because you have <code>DEBUG =
     44  True</code> in your Django settings file. Change that to <code>False</code>,
     45  and only the initial error message will be displayed.  </p>
     46
     47  <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
     48
     49  {% endif %}
     50</body>
     51</html>
     52"""
     53
     54def csrf_failure(request, reason=""):
     55    """
     56    Default view used when request fails CSRF protection
     57    """
     58    t = Template(CSRF_FAILRE_TEMPLATE)
     59    c = Context({'DEBUG': settings.DEBUG,
     60                 'reason': reason})
     61    return HttpResponseForbidden(t.render(c), mimetype='text/html')
  • django/contrib/formtools/templates/formtools/form.html

    diff -r 27807883e23f django/contrib/formtools/templates/formtools/form.html
    a b  
    44
    55{% if form.errors %}<h1>Please correct the following errors</h1>{% else %}<h1>Submit</h1>{% endif %}
    66
    7 <form action="" method="post">
     7<form action="" method="post">{% csrf_token %}
    88<table>
    99{{ form }}
    1010</table>
  • django/contrib/formtools/templates/formtools/preview.html

    diff -r 27807883e23f django/contrib/formtools/templates/formtools/preview.html
    a b  
    1515
    1616<p>Security hash: {{ hash_value }}</p>
    1717
    18 <form action="" method="post">
     18<form action="" method="post">{% csrf_token %}
    1919{% for field in form %}{{ field.as_hidden }}
    2020{% endfor %}
    2121<input type="hidden" name="{{ stage_field }}" value="2" />
     
    2525
    2626<h1>Or edit it again</h1>
    2727
    28 <form action="" method="post">
     28<form action="" method="post">{% csrf_token %}
    2929<table>
    3030{{ form }}
    3131</table>
  • django/contrib/formtools/tests.py

    diff -r 27807883e23f django/contrib/formtools/tests.py
    a b  
    147147
    148148class WizardClass(wizard.FormWizard):
    149149    def render_template(self, *args, **kw):
    150         return ""
     150        return http.HttpResponse("")
    151151
    152152    def done(self, request, cleaned_data):
    153153        return http.HttpResponse(success_string)
    154154
    155 class DummyRequest(object):
     155class DummyRequest(http.HttpRequest):
    156156    def __init__(self, POST=None):
     157        super(DummyRequest, self).__init__()
    157158        self.method = POST and "POST" or "GET"
    158         self.POST = POST
     159        if POST is not None:
     160            self.POST.update(POST)
     161        self.dont_enforce_csrf_checks = True
    159162
    160163class WizardTests(TestCase):
    161164    def test_step_starts_at_zero(self):
  • django/contrib/formtools/wizard.py

    diff -r 27807883e23f django/contrib/formtools/wizard.py
    a b  
    1414from django.utils.hashcompat import md5_constructor
    1515from django.utils.translation import ugettext_lazy as _
    1616from django.contrib.formtools.utils import security_hash
     17from django.contrib.csrf.decorators import csrf_protect
    1718
    1819class FormWizard(object):
    1920    # Dictionary of extra template context variables.
     
    9697                self.step = current_step = next_step
    9798
    9899        return self.render(form, request, current_step)
     100    __call__ = csrf_protect(__call__)
    99101
    100102    def render(self, form, request, step, context=None):
    101103        "Renders the given Form object, returning an HttpResponse."
  • django/template/context.py

    diff -r 27807883e23f django/template/context.py
    a b  
    11from django.core.exceptions import ImproperlyConfigured
    22from django.utils.importlib import import_module
    33
     4# Cache of actual callables.
    45_standard_context_processors = None
     6# We need the CSRF processor no matter what the user has in their settings,
     7# because otherwise it is a security vulnerability, and we can't afford to leave
     8# this to human error or failure to read migration instructions.
     9_builtin_context_processors =  ('django.contrib.csrf.context_processors.csrf',)
    510
    611class ContextPopException(Exception):
    712    "pop() has been called more times than push()"
     
    7580    global _standard_context_processors
    7681    if _standard_context_processors is None:
    7782        processors = []
    78         for path in settings.TEMPLATE_CONTEXT_PROCESSORS:
     83        collect = []
     84        collect.extend(_builtin_context_processors)
     85        collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS)
     86        for path in collect:
    7987            i = path.rfind('.')
    8088            module, attr = path[:i], path[i+1:]
    8189            try:
  • django/template/defaulttags.py

    diff -r 27807883e23f django/template/defaulttags.py
    a b  
    3737    def render(self, context):
    3838        return ''
    3939
     40class CsrfTokenNode(Node):
     41    def render(self, context):
     42        csrf_token = context.get('csrf_token', None)
     43        if csrf_token:
     44            if csrf_token == 'NOTPROVIDED':
     45                return mark_safe(u"")
     46            else:
     47                return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % (csrf_token))
     48        else:
     49            # It's very probable that the token is missing because of
     50            # misconfiguration, so we raise a warning
     51            from django.conf import settings
     52            if settings.DEBUG:
     53                import warnings
     54                warnings.warn("A {% csrf_token %} was used in a template, but the context did not provide the value.  This is usually caused by not using RequestContext.")
     55            return u''
     56
    4057class CycleNode(Node):
    4158    def __init__(self, cyclevars, variable_name=None):
    4259        self.cycle_iter = itertools_cycle(cyclevars)
     
    523540    return node
    524541cycle = register.tag(cycle)
    525542
     543def csrf_token(parser, token):
     544    return CsrfTokenNode()
     545register.tag(csrf_token)
     546
    526547def debug(parser, token):
    527548    """
    528549    Outputs a whole load of debugging information, including the current
  • django/test/client.py

    diff -r 27807883e23f django/test/client.py
    a b  
    6666        signals.request_started.send(sender=self.__class__)
    6767        try:
    6868            request = WSGIRequest(environ)
     69            # sneaky little hack so that we can easily get round
     70            # CsrfViewMiddleware.  This makes life easier, and is probably
     71            # required for backwards compatibility with external tests against
     72            # admin views.
     73            request.dont_enforce_csrf_checks = True
    6974            response = self.get_response(request)
    7075
    7176            # Apply response middleware.
  • docs/intro/tutorial04.txt

    diff -r 27807883e23f docs/intro/tutorial04.txt
    a b  
    2121    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    2222
    2323    <form action="/polls/{{ poll.id }}/vote/" method="post">
     24    {% csrf_token %}
    2425    {% for choice in poll.choice_set.all %}
    2526        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    2627        <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
     
    4647    * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone
    4748      through its loop
    4849
     50    * Since we are creating a POST form (which can have the effect of modifying
     51      data), we unfortunately need to worry about Cross Site Request Forgeries.
     52      Thankfully, you don't have to worry too hard, because Django comes with
     53      very easy-to-use system for protecting against it.  In short, all POST
     54      forms that are targetted at internal URLs need the ``{% csrf_token %}``
     55      template tag adding.  A 'middleware' which is installed by default checks
     56      for the presence of the token in the submitted form data.
     57
     58The ``{% csrf_token %}`` tag requires information from the request object, which
     59is not normally accessible from within the template context.  To fix this, a
     60small adjustment needs to be made to the ``detail`` view, so that it looks like
     61the following::
     62
     63    from django.template import RequestContext
     64    # ...
     65    def detail(request, poll_id):
     66        p = get_object_or_404(Poll, pk=poll_id)
     67        return render_to_response('polls/detail.html', {'poll': p},
     68                                   context_instance=RequestContext(request))
     69
     70The details of how this works are explained in the documentation for
     71:ref:`RequestContext <subclassing-context-requestcontext>`.
     72
    4973Now, let's create a Django view that handles the submitted data and does
    5074something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we
    5175created a URLconf for the polls application that includes this line::
     
    5781    from django.shortcuts import get_object_or_404, render_to_response
    5882    from django.http import HttpResponseRedirect
    5983    from django.core.urlresolvers import reverse
     84    from django.template import RequestContext
    6085    from mysite.polls.models import Choice, Poll
    6186    # ...
    6287    def vote(request, poll_id):
     
    6893            return render_to_response('polls/detail.html', {
    6994                'poll': p,
    7095                'error_message': "You didn't select a choice.",
    71             })
     96            }, context_instance=RequestContext(request))
    7297        else:
    7398            selected_choice.votes += 1
    7499            selected_choice.save()
  • docs/ref/contrib/csrf.txt

    diff -r 27807883e23f docs/ref/contrib/csrf.txt
    a b  
    77.. module:: django.contrib.csrf
    88   :synopsis: Protects against Cross Site Request Forgeries
    99
    10 The CsrfMiddleware class provides easy-to-use protection against
     10The CSRF middleware and template tag provides easy-to-use protection against
    1111`Cross Site Request Forgeries`_.  This type of attack occurs when a malicious
    12 Web site creates a link or form button that is intended to perform some action
    13 on your Web site, using the credentials of a logged-in user who is tricked
    14 into clicking on the link in their browser.
     12Web site contains a link, a form button or some javascript that is intended to
     13perform some action on your Web site, using the credentials of a logged-in user
     14who visits the malicious site in their browser.  A related type of attack,
     15'login CSRF', where an attacking site tricks a user's browser into logging into
     16a site with someone else's credentials, is also covered.
    1517
    16 The first defense against CSRF attacks is to ensure that GET requests
    17 are side-effect free.  POST requests can then be protected by adding this
    18 middleware into your list of installed middleware.
     18The first defense against CSRF attacks is to ensure that GET requests are
     19side-effect free.  POST requests can then be protected by following the steps
     20below.
     21
     22.. versionadded:: 1.2
     23    The 'contrib' apps, including the admin, use the functionality described
     24    here. Because it is security related, a few things have been added to core
     25    functionality to allow this to happen without any required upgrade steps.
    1926
    2027.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
    2128
    2229How to use it
    2330=============
    2431
    25 Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
    26 your list of middleware classes, :setting:`MIDDLEWARE_CLASSES`. It needs to process
    27 the response after the SessionMiddleware, so must come before it in the
    28 list. It also must process the response before things like compression
    29 happen to the response, so it must come after GZipMiddleware in the
    30 list.
     32.. versionchanged:: 1.2
     33    The template tag functionality (the recommended way to use this) was added
     34    in version 1.2. The previous method (still available) is described under
     35    `Legacy method`_.
    3136
    32 The ``CsrfMiddleware`` class is actually composed of two middleware:
    33 ``CsrfViewMiddleware`` which performs the checks on incoming requests,
    34 and ``CsrfResponseMiddleware`` which performs post-processing of the
    35 result.  This allows the individual components to be used and/or
    36 replaced instead of using ``CsrfMiddleware``.
     37To enable CSRF protection for your views, follow these steps:
    3738
    38 .. versionchanged:: 1.1
    39     (previous versions of Django did not provide these two components
    40     of ``CsrfMiddleware`` as described above)
     39    1. Add the middleware
     40       ``'django.contrib.csrf.middleware.CsrfViewMiddleware'`` to your list of
     41       middleware classes, :setting:`MIDDLEWARE_CLASSES`.  (It should come
     42       before ``CsrfResponseMiddleware`` if that is being used, and before any
     43       view middleware that assume that CSRF attacks have been dealt with.)
     44
     45       Alternatively, you can use the decorator
     46       ``django.contrib.csrf.decorators.csrf_protect`` on particular views you
     47       want to protect.  This is **not recommended** by itself, since if you
     48       forget to use it, you will have a security hole.  The 'belt and braces'
     49       strategy of using both is fine, and will incur minimal overhead.
     50
     51    2. In any template that uses a POST form, use the ``csrf_token`` tag inside
     52       the ``<form>`` element if the form is for an internal URL, e.g.::
     53
     54           <form action="" method="POST">{% csrf_token %}
     55
     56       This should not be done for POST forms that target external URLs, since
     57       that would cause the CSRF token to be leaked, leading to a vulnerability.
     58
     59    3. In the corresponding view functions, ensure that the
     60       ``'django.contrib.csrf.context_processors.csrf'`` context processor is
     61       being used. Usually, this can be done in one of two ways:
     62
     63       1. Use RequestContext, which always uses
     64          ``'django.contrib.csrf.context_processors.csrf'`` (no matter what your
     65          TEMPLATE_CONTEXT_PROCESSORS setting).  If you are using
     66          generic views or contrib apps, you are covered already, since these
     67          apps use RequestContext throughout.
     68
     69       2. Manually import and use the processor to generate the CSRF token and
     70          add it to the template context. e.g.::
     71
     72              from django.contrib.csrf.context_processors import csrf
     73              from django.shortcuts import render_to_response
     74
     75              def my_view(request):
     76                  c = {}
     77                  c.update(csrf(request))
     78                  # ... view code here
     79                  return render_to_response("a_template.html", c)
     80
     81          You may want to write your own ``render_to_response`` wrapper that
     82          takes care of this step for you.
     83
     84Legacy method
     85-------------
     86
     87In Django 1.1, the template tag did not exist.  Instead, a post-processing
     88middleware that re-wrote POST forms to include the CRSF token was used.  If you
     89are upgrading a site from version 1.1 or earlier, please read this section and
     90the `Upgrading notes`_ below.  The post-processing middleware is still available
     91as ``CsrfResponseMiddleware``, and it can be used by following these steps:
     92
     93    1. Follow step 1 above to install ``CsrfViewMiddleware``.
     94
     95    2. Add ``'django.contrib.csrf.middleware.CsrfResponseMiddleware'`` to your
     96       :setting:`MIDDLEWARE_CLASSES` setting.
     97
     98       ``CsrfResponseMiddleware`` needs to process the response before things
     99       like compression happen to the response, so it must come after
     100       ``GZipMiddleware`` in the list.  It also must come after
     101       ``CsrfViewMiddleware``.
     102
     103Use of the ``CsrfResponseMiddleware`` is not recommended because of the
     104performance hit it imposes, and because of a potential security problem (see
     105below).  It can be used as an interim measure until applications have been
     106updated to use the ``{% crsf_token %}`` tag.  It is deprecated and will be
     107removed in Django 1.4.
     108
     109Django 1.1 and earlier provided a single ``CsrfMiddleware`` class.  This is also
     110still available for backwards compatibility.  It combines the functions of the
     111two middleware.
     112
     113Note also that previous versions of these classes depended on the sessions
     114framework, but this dependency has now been removed, with backward compatibility
     115support so that upgrading will not produce any issues.
     116
     117Security of legacy method
     118~~~~~~~~~~~~~~~~~~~~~~~~~
     119
     120The post-processing ``CsrfResponseMiddleware`` adds the CSRF token to all POST
     121forms (unless the view has been decorated with ``csrf_response_exempt``).  If
     122the POST form has an external untrusted site as its target, rather than an
     123internal page, that site will be sent the CSRF token when the form is submitted.
     124Armed with this leaked information, that site will then be able to successfully
     125launch a CSRF attack on your site against that user.  The
     126``@csrf_response_exempt`` decorator can be used to fix this, but only if the
     127page doesn't also contain internal forms that require the token.
     128
     129Upgrading notes
     130---------------
     131
     132When upgrading to version 1.2 or later, you may have applications that rely on
     133the old post-processing functionality for CSRF protection, or you may not have
     134enabled any CSRF protection.  This section outlines the steps necessary for a
     135smooth upgrade, without having to fix all the applications to use the new
     136template tag method immediately.
     137
     138If you have ``CsrfMiddleware`` in your :setting:`MIDDLEWARE_CLASSES`, you will now
     139have a working installation with CSRF protection.  It is recommended at this
     140point that you replace ``CsrfMiddleware`` with its two components,
     141``CsrfViewMiddleware`` and ``CsrfResponseMiddleware`` (in that order).
     142
     143If you do not have any of the middleware in your :setting:`MIDDLEWARE_CLASSES`,
     144you will have a working installation but without any CSRF protection for your
     145views (just as you had before). It is strongly recommended to install
     146``CsrfViewMiddleware`` and ``CsrfResponseMiddleware``, as described above.
     147
     148(Note that contrib apps, such as the admin, have been updated to use the
     149``csrf_protect`` decorator, so that they are secured even if you do not add the
     150``CsrfViewMiddleware`` to your settings).
     151
     152Assuming you have followed the above, all views in your Django site will now be
     153protected by the ``CsrfViewMiddleware``.  Contrib apps meet the requirements
     154imposed by the ``CsrfViewMiddleware`` using the template tag, and other
     155applications in your project will meet its requirements by virtue of the
     156``CsrfResponseMiddleware``.
     157
     158The next step is to update all your applications to use the template tag, as
     159described in `How to use it`_, steps 2-3.  This can be done as soon as is
     160practical. Any applications that are updated will now require Django 1.2 or
     161later, since they will use the CSRF template tag library which was not available
     162in earlier versions.
     163
     164Finally, once all applications are upgraded, ``CsrfResponseMiddleware`` can be
     165removed from your settings.
     166
     167While ``CsrfResponseMiddleware`` is still in use, the ``csrf_response_exempt``
     168decorator, described in `Exceptions`_, may be useful.  The post-processing
     169middleware imposes a performance hit and a potential vulnerability, and any
     170views that have been upgraded to use the new template tag method no longer need
     171it.
    41172
    42173Exceptions
    43174----------
    44175
    45176.. versionadded:: 1.1
    46177
    47 To manually exclude a view function from being handled by the
    48 CsrfMiddleware, you can use the ``csrf_exempt`` decorator, found in
    49 the ``django.contrib.csrf.middleware`` module. For example::
     178To manually exclude a view function from being handled by either of the two CSRF
     179middleware, you can use the ``csrf_exempt`` decorator, found in the
     180``django.contrib.csrf.middleware`` module. For example::
    50181
    51182    from django.contrib.csrf.middleware import csrf_exempt
    52183
     
    54185        return HttpResponse('Hello world')
    55186    my_view = csrf_exempt(my_view)
    56187
    57 Like the middleware itself, the ``csrf_exempt`` decorator is composed
    58 of two parts: a ``csrf_view_exempt`` decorator and a
    59 ``csrf_response_exempt`` decorator, found in the same module.  These
    60 disable the view protection mechanism (``CsrfViewMiddleware``) and the
    61 response post-processing (``CsrfResponseMiddleware``) respectively.
    62 They can be used individually if required.
     188Like the middleware, the ``csrf_exempt`` decorator is composed of two parts: a
     189``csrf_view_exempt`` decorator and a ``csrf_response_exempt`` decorator, found
     190in the same module.  These disable the view protection mechanism
     191(``CsrfViewMiddleware``) and the response post-processing
     192(``CsrfResponseMiddleware``) respectively.  They can be used individually if
     193required.
    63194
    64 You don't have to worry about doing this for most AJAX views. Any
    65 request sent with "X-Requested-With: XMLHttpRequest" is automatically
    66 exempt. (See the next section.)
     195You don't have to worry about doing this for most AJAX views. Any request sent
     196with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the next
     197section.)
     198
     199Subdomains
     200----------
     201
     202By default, CSRF cookies are specific to the subdomain they are set for.  This
     203means that a form served from one subdomain (e.g. server1.example.com) will not
     204be able to have a target on another subdomain (e.g. server2.example.com).  This
     205restriction can be removed by setting :setting:`CSRF_COOKIE_DOMAIN` to be
     206something like ``".example.com"``.
     207
     208Please note that, with or without use of this setting, this CSRF protection
     209mechanism is not safe against cross-subdomain attacks -- see `Limitations`_.
     210
     211Rejected requests
     212=================
     213
     214By default, a '403 Forbidden' response is sent to the user if an incoming
     215request fails the checks performed by ``CsrfViewMiddleware``.  This should
     216usually only be seen when there is a genuine Cross Site Request Forgery, or
     217when, due to a programming error, the CSRF token has not been included with a
     218POST form.
     219
     220No logging is done, and the error message is not very friendly, so you may want
     221to provide your own page for handling this condition.  To do this, simply set
     222the :setting:`CSRF_FAILURE_VIEW` setting to a dotted path to your own view
     223function, which should have the following signature::
     224
     225    def csrf_failure(request, reason="")
     226
     227where ``reason`` is a short message (intended for developers or logging, not for
     228end users) indicating the reason the request was rejected.
    67229
    68230How it works
    69231============
    70232
    71 CsrfMiddleware does two things:
     233The CSRF protection is based on the following things:
    72234
    73 1. It modifies outgoing requests by adding a hidden form field to all
    74    'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
    75    a hash of the session ID plus a secret. If there is no session ID set,
    76    this modification of the response isn't done, so there is very little
    77    performance penalty for those requests that don't have a session.
    78    (This is done by ``CsrfResponseMiddleware``).
     2351. A CSRF cookie that is set to a random value (a session independent nonce, as
     236   it is called), which other sites will not have access to.
    79237
    80 2. On all incoming POST requests that have the session cookie set, it
    81    checks that the 'csrfmiddlewaretoken' is present and correct. If it
    82    isn't, the user will get a 403 error. (This is done by
    83    ``CsrfViewMiddleware``)
     238   This cookie is set by ``CsrfViewMiddleware``.  It is meant to be permanent,
     239   but since there is no way to set a cookie that never expires, it is sent with
     240   every response that has called ``django.contrib.csrf.middleware.get_token()``
     241   (the function used internally to retrieve the CSRF token).
    84242
    85 This ensures that only forms that have originated from your Web site
    86 can be used to POST data back.
     2432. A hidden form field with the name 'csrfmiddlewaretoken' present in all
     244   outgoing POST forms.  The value of this field is the value of the CSRF
     245   cookie.
     246
     247   This part is done by the template tag (and with the legacy method, it is done
     248   by ``CsrfResponseMiddleware``).
     249
     2503. For all incoming POST requests, a CSRF cookie must be present, and the
     251   'csrfmiddlewaretoken' field must be present and correct. If it isn't, the
     252   user will get a 403 error.
     253
     254   This check is done by ``CsrfViewMiddleware``.
     255
     2564. In addition, for HTTPS requests, strict referer checking is done by
     257   ``CsrfViewMiddleware``.  This is necessary to address a Man-In-The-Middle
     258   attack that is possible under HTTPS when using a session independent nonce,
     259   due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted
     260   by clients that are talking to a site under HTTPS.  (Referer checking is not
     261   done for HTTP requests, because under HTTP, the presence of the Referer
     262   header is not reliable enough.)
     263
     264This ensures that only forms that have originated from your Web site can be used
     265to POST data back.
    87266
    88267It deliberately only targets HTTP POST requests (and the corresponding POST
    89 forms). GET requests ought never to have any potentially dangerous side
    90 effects (see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a
    91 CSRF attack with a GET request ought to be harmless.
    92 
    93 POST requests that are not accompanied by a session cookie are not protected,
    94 but they do not need to be protected, since the 'attacking' Web site
    95 could make these kind of requests anyway.
    96 
    97 The Content-Type is checked before modifying the response, and only
    98 pages that are served as 'text/html' or 'application/xml+xhtml'
    99 are modified.
     268forms). GET requests ought never to have any potentially dangerous side effects
     269(see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a CSRF attack with a GET
     270request ought to be harmless.
    100271
    101272The middleware tries to be smart about requests that come in via AJAX. Many
    102273JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP header;
    103274these requests are detected and automatically *not* handled by this middleware.
    104275We can do this safely because, in the context of a browser, the header can only
    105276be added by using ``XMLHttpRequest``, and browsers already implement a
    106 same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you
    107 don't trust content within the same domain or subdomains.)
     277same-domain policy for ``XMLHttpRequest``.  For the more recent browsers that
     278relax this same-domain policy, custom headers like "X-Requested-With" are only
     279allowed after the browser has done a 'preflight' check to the server to see if
     280the cross-domain request is allowed, using a strictly 'opt in' mechanism, so
     281this is still safe.
    108282
     283``CsrfResponseMiddleware`` checks the Content-Type before modifying the
     284response, and only pages that are served as 'text/html' or
     285'application/xml+xhtml' are modified.
    109286
    110287.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
    111288
     289Caching
     290=======
     291
     292If the ``csrf_token`` template tag is used by a template (or the ``get_token``
     293function is called some other way), ``CsrfViewMiddleware`` will add a cookie and
     294a ``Vary: Cookie`` header to the response.  Similarly,
     295``CsrfResponseMiddleware`` will send the ``Vary: Cookie`` header if it inserted
     296a token.  This means that these middleware will play well with the cache
     297middleware if it is used as instructed (``UpdateCacheMiddleware`` goes before
     298all other middleware).
     299
     300However, if you use cache decorators on individual views, the CSRF middleware
     301will not yet have been able to set the Vary header.  In this case, on any views
     302that will require a CSRF token to be inserted you should use the
     303:func:`django.views.decorators.vary.vary_on_cookie` decorator first::
     304
     305  from django.views.decorators.cache import cache_page
     306  from django.views.decorators.vary import vary_on_cookie
     307
     308  def my_view(request):
     309      ...
     310
     311  my_view = cache_page(vary_on_cookie(my_view), 60 * 15)
     312
     313Or, using Python 2.4's decorator syntax::
     314
     315  @cache_page(60 * 15)
     316  @vary_on_cookie
     317  def my_view(request):
     318      ...
     319
     320Testing
     321=======
     322
     323The ``CsrfViewMiddleware`` will usually be a big hindrance to testing view
     324functions, due to the need for the CSRF token which must be sent with every POST
     325request.  For this reason, Django's HTTP client for tests has been modified to
     326set a flag on requests which relaxes the middleware and the ``csrf_protect``
     327decorator so that they no longer rejects requests.  In every other respect
     328(e.g. sending cookies etc.), they behave the same.
     329
    112330Limitations
    113331===========
    114332
    115 CsrfMiddleware requires Django's session framework to work. If you have
    116 a custom authentication system that manually sets cookies and the like,
    117 it won't help you.
     333Subdomains within a site will be able to set cookies on the client for the whole
     334domain.  By setting the cookie and using a corresponding token, subdomains will
     335be able to circumvent the CSRF protection.  The only way to avoid this is to
     336ensure that subdomains are controlled by trusted users (or, are at least unable
     337to set cookies).  Note that even without CSRF, there are other vulnerabilities,
     338such as session fixation, that make giving subdomains to untrusted parties a bad
     339idea, and these vulnerabilities cannot easily be fixed with current browsers.
    118340
    119 If your app creates HTML pages and forms in some unusual way, (e.g.
    120 it sends fragments of HTML in JavaScript document.write statements)
    121 you might bypass the filter that adds the hidden field to the form,
    122 in which case form submission will always fail.  It may still be possible
    123 to use the middleware, provided you can find some way to get the
    124 CSRF token and ensure that is included when your form is submitted.
     341If you are using ``CsrfResponseMiddleware`` and your app creates HTML pages and
     342forms in some unusual way, (e.g.  it sends fragments of HTML in JavaScript
     343document.write statements) you might bypass the filter that adds the hidden
     344field to the form, in which case form submission will always fail.  You should
     345use the template tag or :meth:`django.contrib.csrf.middleware.get_token` to get
     346the CSRF token and ensure it is included when your form is submitted.
  • docs/ref/contrib/formtools/form-wizard.txt

    diff -r 27807883e23f docs/ref/contrib/formtools/form-wizard.txt
    a b  
    177177
    178178    {% block content %}
    179179    <p>Step {{ step }} of {{ step_count }}</p>
    180     <form action="." method="post">
     180    <form action="." method="post">{% csrf_token %}
    181181    <table>
    182182    {{ form }}
    183183    </table>
  • docs/ref/settings.txt

    diff -r 27807883e23f docs/ref/settings.txt
    a b  
    144144The default number of seconds to cache a page when the caching middleware or
    145145``cache_page()`` decorator is used.
    146146
     147.. setting:: CSRF_COOKIE_NAME
     148
     149CSRF_COOKIE_NAME
     150----------------
     151Default: ``'csrftoken'``
     152
     153The name of the cookie to use for the CSRF authentication token. This can be whatever you
     154want.  See :ref:`ref-contrib-csrf`.
     155
     156.. setting:: CSRF_COOKIE_DOMAIN
     157
     158CSRF_COOKIE_DOMAIN
     159------------------
     160
     161Default: ``None``
     162
     163The domain to be used when setting the CSRF cookie.  This can be useful for
     164allowing cross-subdomain requests to be exluded from the normal cross site
     165request forgery protection.  It should be set to a string such as
     166``".lawrence.com"`` to allow a POST request from a form on one subdomain to be
     167accepted by accepted by a view served from another subdomain.
     168
     169.. setting:: CSRF_FAILURE_VIEW
     170
     171CSRF_FAILURE_VIEW
     172-----------------
     173
     174Default: ``'django.contrib.csrf.views.csrf_failure'``
     175
     176A dotted path to the view function to be used when an incoming request
     177is rejected by the CSRF protection.  The function should have this signature::
     178
     179  def csrf_failure(request, reason="")
     180
     181where ``reason`` is a short message (intended for developers or logging, not for
     182end users) indicating the reason the request was rejected.  See
     183:ref:`ref-contrib-csrf`.
     184
    147185.. setting:: DATABASE_ENGINE
    148186
    149187DATABASE_ENGINE
     
    751789
    752790    ('django.middleware.common.CommonMiddleware',
    753791     'django.contrib.sessions.middleware.SessionMiddleware',
     792     'django.contrib.csrf.middleware.CsrfViewMiddleware',
    754793     'django.contrib.auth.middleware.AuthenticationMiddleware',)
    755794
    756795A tuple of middleware classes to use. See :ref:`topics-http-middleware`.
  • docs/ref/templates/api.txt

    diff -r 27807883e23f docs/ref/templates/api.txt
    a b  
    313313    "django.core.context_processors.i18n",
    314314    "django.core.context_processors.media")
    315315
     316.. versionadded:: 1.2
     317   In addition to these, ``RequestContext`` always uses
     318   ``'django.contrib.csrf.context_processors.csrf'``.  This is a security
     319   related context processor required by the admin and other contrib apps, and,
     320   in case of accidental misconfiguration, it is deliberately hardcoded in and
     321   cannot be turned off by the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
     322
    316323Each processor is applied in order. That means, if one processor adds a
    317324variable to the context and a second processor adds a variable with the same
    318325name, the second will override the first. The default processors are explained
     
    404411``RequestContext`` will contain a variable ``MEDIA_URL``, providing the
    405412value of the :setting:`MEDIA_URL` setting.
    406413
     414django.contrib.csrf.context_processors.csrf
     415~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     416
     417.. versionadded:: 1.2
     418
     419This processor adds a token that is needed by the ``csrf_token`` template tag
     420for protection against :ref:`Cross Site Request Forgeries <ref-contrib-csrf>`.
     421
    407422django.core.context_processors.request
    408423~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    409424
  • docs/ref/templates/builtins.txt

    diff -r 27807883e23f docs/ref/templates/builtins.txt
    a b  
    5353
    5454.. templatetag:: cycle
    5555
     56csrf_token
     57~~~~~~~~~~
     58
     59.. versionadded:: 1.2
     60
     61This is described in the documentation for :ref:`Cross Site Request Forgeries <ref-contrib-csrf>`.
     62
    5663cycle
    5764~~~~~
    5865
  • docs/topics/auth.txt

    diff -r 27807883e23f docs/topics/auth.txt
    a b  
    767767        <p>Your username and password didn't match. Please try again.</p>
    768768        {% endif %}
    769769
    770         <form method="post" action="{% url django.contrib.auth.views.login %}">
     770        <form method="post" action="{% url django.contrib.auth.views.login %}">{% csrf_token %}
    771771        <table>
    772772        <tr>
    773773            <td>{{ form.username.label_tag }}</td>
  • docs/topics/http/middleware.txt

    diff -r 27807883e23f docs/topics/http/middleware.txt
    a b  
    2929    MIDDLEWARE_CLASSES = (
    3030        'django.middleware.common.CommonMiddleware',
    3131        'django.contrib.sessions.middleware.SessionMiddleware',
     32        'django.contrib.csrf.middleware.CsrfViewMiddleware',
    3233        'django.contrib.auth.middleware.AuthenticationMiddleware',
    3334    )
    3435
  • tests/regressiontests/admin_views/tests.py

    diff -r 27807883e23f tests/regressiontests/admin_views/tests.py
    a b  
    885885        # 4 action inputs (3 regular checkboxes, 1 checkbox to select all)
    886886        # main form submit button = 1
    887887        # search field and search submit button = 2
    888         # 6 + 2 + 1 + 2 = 11 inputs
    889         self.failUnlessEqual(response.content.count("<input"), 15)
     888        # CSRF field = 1
     889        # 6 + 2 + 4 + 1 + 2 + 1 = 16 inputs
     890        self.failUnlessEqual(response.content.count("<input"), 16)
    890891        # 1 select per object = 3 selects
    891892        self.failUnlessEqual(response.content.count("<select"), 4)
    892893
Back to Top