Index: django/conf/project_template/settings.py
===================================================================
--- django/conf/project_template/settings.py	(revision 10618)
+++ django/conf/project_template/settings.py	(working copy)
@@ -60,6 +60,7 @@
 MIDDLEWARE_CLASSES = (
     'django.middleware.common.CommonMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.csrf.middleware.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
 )
 
@@ -74,6 +75,7 @@
 INSTALLED_APPS = (
     'django.contrib.auth',
     'django.contrib.contenttypes',
+    'django.contrib.csrf',
     'django.contrib.sessions',
     'django.contrib.sites',
 )
Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py	(revision 10618)
+++ django/conf/global_settings.py	(working copy)
@@ -165,6 +165,7 @@
     'django.core.context_processors.debug',
     'django.core.context_processors.i18n',
     'django.core.context_processors.media',
+    'django.contrib.csrf.context_processors.csrf',
 #    'django.core.context_processors.request',
 )
 
@@ -303,6 +304,7 @@
 MIDDLEWARE_CLASSES = (
     'django.middleware.common.CommonMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.csrf.middleware.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
 #     'django.middleware.http.ConditionalGetMiddleware',
 #     'django.middleware.gzip.GZipMiddleware',
@@ -377,6 +379,14 @@
 # The number of days a password reset link is valid for
 PASSWORD_RESET_TIMEOUT_DAYS = 3
 
+########
+# CSRF #
+########
+
+# Dotted path to callable to be used as view when a request is
+# rejected by the CSRF middleware.
+CSRF_FAILURE_VIEW = 'django.contrib.csrf.views.csrf_failure'
+
 ###########
 # TESTING #
 ###########
Index: django/contrib/formtools/templates/formtools/preview.html
===================================================================
--- django/contrib/formtools/templates/formtools/preview.html	(revision 10618)
+++ django/contrib/formtools/templates/formtools/preview.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-
+{% load csrf %}
 {% block content %}
 
 <h1>Preview your submission</h1>
@@ -15,7 +15,7 @@
 
 <p>Security hash: {{ hash_value }}</p>
 
-<form action="" method="post">
+<form action="" method="post">{% csrf_token %}
 {% for field in form %}{{ field.as_hidden }}
 {% endfor %}
 <input type="hidden" name="{{ stage_field }}" value="2" />
@@ -25,7 +25,7 @@
 
 <h1>Or edit it again</h1>
 
-<form action="" method="post">
+<form action="" method="post">{% csrf_token %}
 <table>
 {{ form }}
 </table>
Index: django/contrib/formtools/templates/formtools/form.html
===================================================================
--- django/contrib/formtools/templates/formtools/form.html	(revision 10618)
+++ django/contrib/formtools/templates/formtools/form.html	(working copy)
@@ -1,10 +1,10 @@
 {% extends "base.html" %}
-
+{% load csrf %}
 {% block content %}
 
 {% if form.errors %}<h1>Please correct the following errors</h1>{% else %}<h1>Submit</h1>{% endif %}
 
-<form action="" method="post">
+<form action="" method="post">{% csrf_token %}
 <table>
 {{ form }}
 </table>
Index: django/contrib/comments/templates/comments/approve.html
===================================================================
--- django/contrib/comments/templates/comments/approve.html	(revision 10618)
+++ django/contrib/comments/templates/comments/approve.html	(working copy)
@@ -1,12 +1,12 @@
 {% extends "comments/base.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block title %}{% trans "Approve a comment" %}{% endblock %}
 
 {% block content %}
   <h1>{% trans "Really make this comment public?" %}</h1>
   <blockquote>{{ comment|linebreaks }}</blockquote>
-  <form action="." method="post">
+  <form action="." method="post">{% csrf_token %}
     {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %}
     <p class="submit">
       <input type="submit" name="submit" value="{% trans "Approve" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a>
Index: django/contrib/comments/templates/comments/preview.html
===================================================================
--- django/contrib/comments/templates/comments/preview.html	(revision 10618)
+++ django/contrib/comments/templates/comments/preview.html	(working copy)
@@ -1,11 +1,11 @@
 {% extends "comments/base.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block title %}{% trans "Preview your comment" %}{% endblock %}
 
 {% block content %}
   {% load comments %}
-  <form action="{% comment_form_target %}" method="post">
+  <form action="{% comment_form_target %}" method="post">{% csrf_token %}
     {% if next %}<input type="hidden" name="next" value="{{ next }}" />{% endif %}
     {% if form.errors %}
     <h1>{% blocktrans count form.errors|length as counter %}Please correct the error below{% plural %}Please correct the errors below{% endblocktrans %}</h1>
Index: django/contrib/comments/templates/comments/delete.html
===================================================================
--- django/contrib/comments/templates/comments/delete.html	(revision 10618)
+++ django/contrib/comments/templates/comments/delete.html	(working copy)
@@ -1,12 +1,12 @@
 {% extends "comments/base.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block title %}{% trans "Remove a comment" %}{% endblock %}
 
 {% block content %}
 <h1>{% trans "Really remove this comment?" %}</h1>
   <blockquote>{{ comment|linebreaks }}</blockquote>
-  <form action="." method="post">
+  <form action="." method="post">{% csrf_token %}
     {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %}
     <p class="submit">
     <input type="submit" name="submit" value="{% trans "Remove" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a>
Index: django/contrib/comments/templates/comments/form.html
===================================================================
--- django/contrib/comments/templates/comments/form.html	(revision 10618)
+++ django/contrib/comments/templates/comments/form.html	(working copy)
@@ -1,5 +1,5 @@
-{% load comments i18n %}
-<form action="{% comment_form_target %}" method="post">
+{% load comments i18n csrf %}
+<form action="{% comment_form_target %}" method="post">{% csrf_token %}
   {% if next %}<input type="hidden" name="next" value="{{ next }}" />{% endif %}
   {% for field in form %}
     {% if field.is_hidden %}
Index: django/contrib/comments/templates/comments/moderation_queue.html
===================================================================
--- django/contrib/comments/templates/comments/moderation_queue.html	(revision 10618)
+++ django/contrib/comments/templates/comments/moderation_queue.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/change_list.html" %}
-{% load adminmedia i18n %}
+{% load adminmedia i18n csrf %}
 
 {% block title %}{% trans "Comment moderation queue" %}{% endblock %}
 
@@ -44,11 +44,11 @@
       {% for comment in comments %}
         <tr class="{% cycle 'row1' 'row2' %}">
           <td class="actions">
-            <form action="{% url comments-approve comment.pk %}" method="post">
+            <form action="{% url comments-approve comment.pk %}" method="post">{% csrf_token %}
               <input type="hidden" name="next" value="{% url comments-moderation-queue %}" />
               <input class="approve submit" type="submit" name="submit" value="{% trans "Approve" %}" />
             </form>
-            <form action="{% url comments-delete comment.pk %}" method="post">
+            <form action="{% url comments-delete comment.pk %}" method="post">{% csrf_token %}
               <input type="hidden" name="next" value="{% url comments-moderation-queue %}" />
               <input class="remove submit" type="submit" name="submit" value="{% trans "Remove" %}" />
             </form>
Index: django/contrib/comments/templates/comments/flag.html
===================================================================
--- django/contrib/comments/templates/comments/flag.html	(revision 10618)
+++ django/contrib/comments/templates/comments/flag.html	(working copy)
@@ -1,12 +1,12 @@
 {% extends "comments/base.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block title %}{% trans "Flag this comment" %}{% endblock %}
 
 {% block content %}
 <h1>{% trans "Really flag this comment?" %}</h1>
   <blockquote>{{ comment|linebreaks }}</blockquote>
-  <form action="." method="post">
+  <form action="." method="post">{% csrf_token %}
     {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %}
     <p class="submit">
     <input type="submit" name="submit" value="{% trans "Flag" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a>
Index: django/contrib/admin/templates/admin/template_validator.html
===================================================================
--- django/contrib/admin/templates/admin/template_validator.html	(revision 10618)
+++ django/contrib/admin/templates/admin/template_validator.html	(working copy)
@@ -1,10 +1,10 @@
 {% extends "admin/base_site.html" %}
-
+{% load csrf %}
 {% block content %}
 
 <div id="content-main">
 
-<form action="" method="post">
+<form action="" method="post">{% csrf_token %}
 
 {% if form.errors %}
 <p class="errornote">Your template had {{ form.errors|length }} error{{ form.errors|pluralize }}:</p>
Index: django/contrib/admin/templates/admin/change_list.html
===================================================================
--- django/contrib/admin/templates/admin/change_list.html	(revision 10618)
+++ django/contrib/admin/templates/admin/change_list.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load adminmedia admin_list i18n %}
+{% load adminmedia admin_list i18n csrf %}
 
 {% block extrastyle %}
   {{ block.super }}
@@ -68,7 +68,7 @@
         {% endif %}
       {% endblock %}
       
-      <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>
+      <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>{% csrf_token %}
       {% if cl.formset %}
         {{ cl.formset.management_form }}
       {% endif %}
Index: django/contrib/admin/templates/admin/delete_selected_confirmation.html
===================================================================
--- django/contrib/admin/templates/admin/delete_selected_confirmation.html	(revision 10618)
+++ django/contrib/admin/templates/admin/delete_selected_confirmation.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block breadcrumbs %}
 <div class="breadcrumbs">
@@ -23,7 +23,7 @@
     {% for deleteable_object in deletable_objects %}
         <ul>{{ deleteable_object|unordered_list }}</ul>
     {% endfor %}
-    <form action="" method="post">
+    <form action="" method="post">{% csrf_token %}
     <div>
     {% for obj in queryset %}
     <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk }}" />
@@ -34,4 +34,4 @@
     </div>
     </form>
 {% endif %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
Index: django/contrib/admin/templates/admin/auth/user/change_password.html
===================================================================
--- django/contrib/admin/templates/admin/auth/user/change_password.html	(revision 10618)
+++ django/contrib/admin/templates/admin/auth/user/change_password.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_modify adminmedia %}
+{% load i18n admin_modify adminmedia csrf %}
 {% block extrahead %}{{ block.super }}
 <script type="text/javascript" src="../../../../jsi18n/"></script>
 {% endblock %}
@@ -15,7 +15,7 @@
 </div>
 {% endif %}{% endblock %}
 {% block content %}<div id="content-main">
-<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
+<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
 <div>
 {% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
 {% if form.errors %}
Index: django/contrib/admin/templates/admin/login.html
===================================================================
--- django/contrib/admin/templates/admin/login.html	(revision 10618)
+++ django/contrib/admin/templates/admin/login.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block extrastyle %}{% load adminmedia %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/login.css" />{% endblock %}
 
@@ -14,7 +14,7 @@
 <p class="errornote">{{ error_message }}</p>
 {% endif %}
 <div id="content-main">
-<form action="{{ app_path }}" method="post" id="login-form">
+<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
   <div class="form-row">
     <label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
   </div>
Index: django/contrib/admin/templates/admin/change_form.html
===================================================================
--- django/contrib/admin/templates/admin/change_form.html	(revision 10618)
+++ django/contrib/admin/templates/admin/change_form.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n admin_modify adminmedia %}
+{% load i18n admin_modify adminmedia csrf %}
 
 {% block extrahead %}{{ block.super }}
 <script type="text/javascript" src="../../../jsi18n/"></script>
@@ -29,7 +29,7 @@
   </ul>
 {% endif %}{% endif %}
 {% endblock %}
-<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
+<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 %}
 <div>
 {% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
 {% if save_on_top %}{% submit_row %}{% endif %}
Index: django/contrib/admin/templates/admin/delete_confirmation.html
===================================================================
--- django/contrib/admin/templates/admin/delete_confirmation.html	(revision 10618)
+++ django/contrib/admin/templates/admin/delete_confirmation.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block breadcrumbs %}
 <div class="breadcrumbs">
@@ -22,7 +22,7 @@
 {% else %}
     <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>
     <ul>{{ deleted_objects|unordered_list }}</ul>
-    <form action="" method="post">
+    <form action="" method="post">{% csrf_token %}
     <div>
     <input type="hidden" name="post" value="yes" />
     <input type="submit" value="{% trans "Yes, I'm sure" %}" />
Index: django/contrib/admin/templates/registration/password_reset_confirm.html
===================================================================
--- django/contrib/admin/templates/registration/password_reset_confirm.html	(revision 10618)
+++ django/contrib/admin/templates/registration/password_reset_confirm.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset confirmation' %}</div>{% endblock %}
 
@@ -13,7 +13,7 @@
 
 <p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
 
-<form action="" method="post">
+<form action="" method="post">{% csrf_token %}
 {{ form.new_password1.errors }}
 <p class="aligned wide"><label for="id_new_password1">{% trans 'New password:' %}</label>{{ form.new_password1 }}</p>
 {{ form.new_password2.errors }}
Index: django/contrib/admin/templates/registration/password_reset_form.html
===================================================================
--- django/contrib/admin/templates/registration/password_reset_form.html	(revision 10618)
+++ django/contrib/admin/templates/registration/password_reset_form.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
 
@@ -11,7 +11,7 @@
 
 <p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." %}</p>
 
-<form action="" method="post">
+<form action="" method="post">{% csrf_token %}
 {{ form.email.errors }}
 <p><label for="id_email">{% trans 'E-mail address:' %}</label> {{ form.email }} <input type="submit" value="{% trans 'Reset my password' %}" /></p>
 </form>
Index: django/contrib/admin/templates/registration/password_change_form.html
===================================================================
--- django/contrib/admin/templates/registration/password_change_form.html	(revision 10618)
+++ django/contrib/admin/templates/registration/password_change_form.html	(working copy)
@@ -1,5 +1,5 @@
 {% extends "admin/base_site.html" %}
-{% load i18n %}
+{% load i18n csrf %}
 {% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %} {% trans 'Change password' %} / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
 
@@ -11,7 +11,7 @@
 
 <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>
 
-<form action="" method="post">
+<form action="" method="post">{% csrf_token %}
 
 {{ form.old_password.errors }}
 <p class="aligned wide"><label for="id_old_password">{% trans 'Old password:' %}</label>{{ form.old_password }}</p>
Index: django/contrib/admin/sites.py
===================================================================
--- django/contrib/admin/sites.py	(revision 10618)
+++ django/contrib/admin/sites.py	(working copy)
@@ -3,6 +3,7 @@
 from django.contrib.admin import ModelAdmin
 from django.contrib.admin import actions
 from django.contrib.auth import authenticate, login
+from django.contrib.csrf.middleware import csrf_response_exempt
 from django.db.models.base import ModelBase
 from django.core.exceptions import ImproperlyConfigured
 from django.shortcuts import render_to_response
@@ -156,8 +157,12 @@
             raise ImproperlyConfigured("Put 'django.contrib.admin' in your INSTALLED_APPS setting in order to use the admin application.")
         if not ContentType._meta.installed:
             raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in your INSTALLED_APPS setting in order to use the admin application.")
+        if 'django.contrib.csrf' not in settings.INSTALLED_APPS:
+            raise ImproperlyConfigured("Put 'django.contrib.csrf' in your INSTALLED_APPS setting in order to use the admin application.")
         if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
             raise ImproperlyConfigured("Put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
+        if 'django.contrib.csrf.context_processors.csrf' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
+            raise ImproperlyConfigured("Put 'django.contrib.csrf.context_processors.csrf' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
 
     def admin_view(self, view):
         """
@@ -182,7 +187,8 @@
             if not self.has_permission(request):
                 return self.login(request)
             return view(request, *args, **kwargs)
-        return update_wrapper(inner, view)
+        inner = update_wrapper(inner, view)
+        return csrf_response_exempt(inner)
 
     def get_urls(self):
         from django.conf.urls.defaults import patterns, url, include
Index: django/contrib/csrf/middleware.py
===================================================================
--- django/contrib/csrf/middleware.py	(revision 10618)
+++ django/contrib/csrf/middleware.py	(working copy)
@@ -5,94 +5,148 @@
 against request forgeries from other sites.
 """
 
+import itertools
 import re
-import itertools
+import random
 try:
     from functools import wraps
 except ImportError:
     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
 
 from django.conf import settings
-from django.http import HttpResponseForbidden
+from django.core.urlresolvers import get_callable
 from django.utils.hashcompat import md5_constructor
 from django.utils.safestring import mark_safe
 
-_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>')
-
 _POST_FORM_RE = \
     re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
 
 _HTML_TYPES = ('text/html', 'application/xhtml+xml')
 
-def _make_token(session_id):
+def _get_failure_view():
+    """
+    Returns the view to be used for CSRF rejections
+    """
+    return get_callable(settings.CSRF_FAILURE_VIEW)
+
+# Use the system (hardware-based) random number generator if it exists.
+if hasattr(random, 'SystemRandom'):
+    randrange = random.SystemRandom().randrange
+else:
+    randrange = random.randrange
+_MAX_CSRF_KEY = 18446744073709551616L     # 2 << 63
+
+def _get_new_csrf_key():
+    return md5_constructor("%s%s"
+                % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
+
+def _make_legacy_session_token(session_id):
     return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
 
+def _make_token(csrf_cookie):
+    return md5_constructor("csrf-" + settings.SECRET_KEY + csrf_cookie).hexdigest()
+
+def _cookie_name():
+    # Backwards-compatibility: derive the cookie name from SESSION_COOKIE_NAME, so if the
+    # user has specified a unique session cookie, we use a unique CSRF cookie, too.
+    return "csrf_%s" % settings.SESSION_COOKIE_NAME
+
+def get_token(request):
+    """
+    Returns the the CSRF token required for a POST form.
+    """
+    csrf_cookie = request.META.get("CSRF_COOKIE", None)
+    # This function is called by the csrf_token template tag. So, we don't make
+    # the CSRF middleware a requirement, otherwise people would have to change
+    # their templates if they disabled the middleware or used @csrf_view_exempt
+    if csrf_cookie is None:
+        return None
+
+    return _make_token(csrf_cookie)
+
 class CsrfViewMiddleware(object):
     """
     Middleware that requires a present and correct csrfmiddlewaretoken
-    for POST requests that have an active session.
+    for POST requests that have a CSRF cookie, and sets an outgoing
+    CSRF cookie.
+
+    This middleware should be used in conjunction with the csrf_token template
+    tag.
     """
     def process_view(self, request, callback, callback_args, callback_kwargs):
+        if getattr(callback, 'csrf_exempt', False):
+            return None
+
+        # If the user doesn't have a CSRF cookie, generate one and store it in the
+        # request, so it's available to the view.  We'll store it in a cookie when
+        # we reach the response.
+        try:
+            request.META["CSRF_COOKIE"] = request.COOKIES[_cookie_name()]
+            cookie_is_new = False
+        except KeyError:
+            # No cookie, so create one.
+            request.META["CSRF_COOKIE"] = _get_new_csrf_key()
+            cookie_is_new = True
+
         if request.method == 'POST':
-            if getattr(callback, 'csrf_exempt', False):
-                return None
-
             if request.is_ajax():
                 return None
 
-            try:
-                session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
-            except KeyError:
-                # No session, no check required
-                return None
+            # If the user didn't already have a CSRF key, then accept the
+            # session key for the middleware token, so CSRF protection isn't lost
+            # for the period between upgrading to CSRF cookes to the first time
+            # each user comes back to the site to receive one.
+            if cookie_is_new:
+                try:
+                    session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
+                    csrf_token = _make_legacy_session_token(session_id)
+                except KeyError:
+                    # No CSRF cookie and no session cookie; no check is performed.
+                    return None
+            else:
+                csrf_cookie = request.META["CSRF_COOKIE"]
+                csrf_token = _make_token(csrf_cookie)
 
-            csrf_token = _make_token(session_id)
             # check incoming token
-            try:
-                request_csrf_token = request.POST['csrfmiddlewaretoken']
-            except KeyError:
-                return HttpResponseForbidden(_ERROR_MSG)
-
+            request_csrf_token = request.POST.get('csrfmiddlewaretoken', None)
             if request_csrf_token != csrf_token:
-                return HttpResponseForbidden(_ERROR_MSG)
+                return _get_failure_view()(request)
 
         return None
 
+    def process_response(self, request, response):
+        # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
+        # never called, probaby because a request middleware returned a response
+        # (for example, contrib.auth redirecting to a login page).
+        if request.META.get("CSRF_COOKIE") is None:
+            return response
+
+        # Set the CSRF cookie even if it's already set, so we renew the expiry timer.
+        response.set_cookie(_cookie_name(),
+                request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
+                domain=settings.SESSION_COOKIE_DOMAIN,
+                path=settings.SESSION_COOKIE_PATH)
+
+        return response
+
 class CsrfResponseMiddleware(object):
     """
-    Middleware that post-processes a response to add a
-    csrfmiddlewaretoken if the response/request have an active
-    session.
+    Middleware that post-processes a response to add a csrfmiddlewaretoken.
+
+    It is recommended to use the csrf_token template tag instead of
+    this middleware.
     """
     def process_response(self, request, response):
         if getattr(response, 'csrf_exempt', False):
             return response
 
-        csrf_token = None
-        try:
-            # This covers a corner case in which the outgoing response
-            # both contains a form and sets a session cookie.  This
-            # really should not be needed, since it is best if views
-            # that create a new session (login pages) also do a
-            # redirect, as is done by all such view functions in
-            # Django.
-            cookie = response.cookies[settings.SESSION_COOKIE_NAME]
-            csrf_token = _make_token(cookie.value)
-        except KeyError:
-            # Normal case - look for existing session cookie
-            try:
-                session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
-                csrf_token = _make_token(session_id)
-            except KeyError:
-                # no incoming or outgoing cookie
-                pass
+        if response['Content-Type'].split(';')[0] in _HTML_TYPES:
+            csrf_token = get_token(request)
+            assert csrf_token is not None, 'META["CSRF_COOKIE"] is not set.'
 
-        if csrf_token is not None and \
-                response['Content-Type'].split(';')[0] in _HTML_TYPES:
-
             # ensure we don't add the 'id' attribute twice (HTML validity)
             idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
-                                            itertools.repeat(''))
+                                           itertools.repeat(''))
             def add_csrf_field(match):
                 """Returns the matched <form> tag plus the added <input> element"""
                 return mark_safe(match.group() + "<div style='display:none;'>" + \
@@ -104,28 +158,31 @@
             response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
         return response
 
-class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware):
+class CsrfMiddleware(object):
     """Django middleware that adds protection against Cross Site
     Request Forgeries by adding hidden form fields to POST forms and
     checking requests for the correct value.
 
-    In the list of middlewares, SessionMiddleware is required, and
-    must come after this middleware.  CsrfMiddleWare must come after
-    compression middleware.
+    CsrfMiddleware uses two middleware, CsrfViewMiddleware and
+    CsrfResponseMiddleware, which can be used independently.  It is recommended
+    to use only CsrfViewMiddleware and use the csrf_token template tag in
+    templates for inserting the token.
+    """
+    # We can't just inherit from CsrfViewMiddleware and CsrfResponseMiddleware
+    # because both have process_response methods.
+    def __init__(self):
+        self.response_middleware = CsrfResponseMiddleware()
+        self.view_middleware = CsrfViewMiddleware()
 
-    If a session ID cookie is present, it is hashed with the
-    SECRET_KEY setting to create an authentication token.  This token
-    is added to all outgoing POST forms and is expected on all
-    incoming POST requests that have a session ID cookie.
+    def process_response(self, request, resp):
+        # We must do the cookie setting before the post-processing
+        resp2 = self.view_middleware.process_response(request, resp)
+        return self.response_middleware.process_response(request, resp2)
 
-    If you are setting cookies directly, instead of using Django's
-    session framework, this middleware will not work.
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        return self.view_middleware.process_view(request, callback, callback_args,
+                                                 callback_kwargs)
 
-    CsrfMiddleWare is composed of two middleware, CsrfViewMiddleware
-    and CsrfResponseMiddleware which can be used independently.
-    """
-    pass
-
 def csrf_response_exempt(view_func):
     """
     Modifies a view function so that its response is exempt
Index: django/contrib/csrf/views.py
===================================================================
--- django/contrib/csrf/views.py	(revision 0)
+++ django/contrib/csrf/views.py	(revision 0)
@@ -0,0 +1,10 @@
+from django.http import HttpResponseForbidden
+from django.utils.safestring import mark_safe
+
+_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>')
+
+def csrf_failure(request):
+    """
+    Default view used when request fails CSRF protection
+    """
+    return HttpResponseForbidden(_ERROR_MSG)

Property changes on: django/contrib/csrf/views.py
___________________________________________________________________
Added: svn:eol-style
   + native

Index: django/contrib/csrf/tests.py
===================================================================
--- django/contrib/csrf/tests.py	(revision 10618)
+++ django/contrib/csrf/tests.py	(working copy)
@@ -2,143 +2,248 @@
 
 from django.test import TestCase
 from django.http import HttpRequest, HttpResponse, HttpResponseForbidden
-from django.contrib.csrf.middleware import CsrfMiddleware, _make_token, csrf_exempt
+from django.contrib.csrf.middleware import CsrfMiddleware, CsrfViewMiddleware, _make_token, _cookie_name, csrf_exempt
+from django.contrib.csrf.context_processors import csrf
+from django.contrib.sessions.middleware import SessionMiddleware
+from django.utils.importlib import import_module
 from django.conf import settings
+from django.template import RequestContext, Template
 
-
+# Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests
 def post_form_response():
     resp = HttpResponse(content="""
 <html><body><form method="POST"><input type="text" /></form></body></html>
 """, mimetype="text/html")
     return resp
 
-def test_view(request):
+def post_form_response_non_html():
+    resp = post_form_response()
+    resp["Content-Type"] = "application/xml"
+    return resp
+
+def post_form_view(request):
     return post_form_response()
 
+# Response/views used for template tag tests
+def _token_template():
+    return Template("{% load csrf %}{% csrf_token %}")
+
+def _render_csrf_token_template(req):
+    context = RequestContext(req, processors=[csrf])
+    template = _token_template()
+    return template.render(context)
+
+def token_view(request):
+    return HttpResponse(_render_csrf_token_template(request))
+
 class CsrfMiddlewareTest(TestCase):
+    _csrf_id = "1"
 
+    # This is a valid session token for this ID and secret key.  This was generated using
+    # the old code that we're to be backwards-compatible with.  Don't use the CSRF code
+    # to generate this hash, or we're merely testing the code against itself and not
+    # checking backwards-compatibility.  This is also the output of (echo -n test1 | md5sum).
+    _session_token = "5a105e8b9d40e1329780d62ea2265d8a"
     _session_id = "1"
+    _secret_key_for_session_test= "test"
 
-    def _get_GET_no_session_request(self):
+    def _get_GET_no_csrf_cookie_request(self):
         return HttpRequest()
 
-    def _get_GET_session_request(self):
-        req = self._get_GET_no_session_request()
-        req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
+    def _get_GET_csrf_cookie_request(self):
+        req = HttpRequest()
+        req.COOKIES[_cookie_name()] = self._csrf_id
         return req
 
-    def _get_POST_session_request(self):
-        req = self._get_GET_session_request()
+    def _get_POST_csrf_cookie_request(self):
+        req = self._get_GET_csrf_cookie_request()
         req.method = "POST"
         return req
 
-    def _get_POST_no_session_request(self):
-        req = self._get_GET_no_session_request()
+    def _get_POST_no_csrf_cookie_request(self):
+        req = self._get_GET_no_csrf_cookie_request()
         req.method = "POST"
         return req
 
+    def _get_POST_request_with_token(self):
+        req = self._get_POST_csrf_cookie_request()
+        req.POST['csrfmiddlewaretoken'] = _make_token(self._csrf_id)
+        return req
+
     def _get_POST_session_request_with_token(self):
-        req = self._get_POST_session_request()
-        req.POST['csrfmiddlewaretoken'] = _make_token(self._session_id)
+        req = self._get_POST_no_csrf_cookie_request()
+        req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
+        req.POST['csrfmiddlewaretoken'] = self._session_token
         return req
 
-    def _get_post_form_response(self):
-        return post_form_response()
+    def _get_POST_session_request_no_token(self):
+        req = self._get_POST_no_csrf_cookie_request()
+        req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
+        return req
 
-    def _get_new_session_response(self):
-        resp = self._get_post_form_response()
-        resp.cookies[settings.SESSION_COOKIE_NAME] = self._session_id
-        return resp
+    def _check_token_present(self, response, csrf_id=None):
+        self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % _make_token(csrf_id or self._csrf_id))
 
-    def _check_token_present(self, response):
-        self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % _make_token(self._session_id))
-
-    def get_view(self):
-        return test_view
-
-    # Check the post processing
-    def test_process_response_no_session(self):
+    # Check the post processing and outgoing cookie
+    def test_process_response_no_csrf_cookie(self):
         """
-        Check the the post-processor does nothing if no session active
+        When no prior CSRF cookie exists, check that the cookie is created and a
+        token is inserted.
         """
-        req = self._get_GET_no_session_request()
-        resp = self._get_post_form_response()
+        req = self._get_GET_no_csrf_cookie_request()
+        CsrfMiddleware().process_view(req, post_form_view, (), {})
+
+        resp = post_form_response()
         resp_content = resp.content # needed because process_response modifies resp
         resp2 = CsrfMiddleware().process_response(req, resp)
-        self.assertEquals(resp_content, resp2.content)
 
-    def test_process_response_existing_session(self):
+        csrf_cookie = resp2.cookies.get(_cookie_name(), False)
+        self.assertNotEqual(csrf_cookie, False)
+        self.assertNotEqual(resp_content, resp2.content)
+        self._check_token_present(resp2, csrf_cookie.value)
+
+    def test_process_response_no_csrf_cookie_view_only(self):
         """
-        Check that the token is inserted if there is an existing session
+        When no prior CSRF cookie exists, check that the cookie is created, even
+        if only CsrfViewMiddleware is used.
         """
-        req = self._get_GET_session_request()
-        resp = self._get_post_form_response()
+        # The CsrfViewMiddleware must have the cookie setting code.
+        req = self._get_GET_no_csrf_cookie_request()
+        CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+        resp = post_form_view(req)
+        resp2 = CsrfViewMiddleware().process_response(req, resp)
+
+        csrf_cookie = resp2.cookies.get(_cookie_name(), False)
+        self.assertNotEqual(csrf_cookie, False)
+
+    def test_process_response_existing_csrf_cookie(self):
+        """
+        Check that the token is inserted when a prior CSRF cookie exists
+        """
+        req = self._get_GET_csrf_cookie_request()
+        CsrfMiddleware().process_view(req, post_form_view, (), {})
+
+        resp = post_form_response()
         resp_content = resp.content # needed because process_response modifies resp
         resp2 = CsrfMiddleware().process_response(req, resp)
         self.assertNotEqual(resp_content, resp2.content)
         self._check_token_present(resp2)
 
-    def test_process_response_new_session(self):
+    def test_process_response_non_html(self):
         """
         Check that the token is inserted if there is a new session being started
+        Check the the post-processor does nothing for content-types not in _HTML_TYPES.
         """
-        req = self._get_GET_no_session_request() # no session in request
-        resp = self._get_new_session_response() # but new session started
+        req = self._get_GET_no_csrf_cookie_request()
+        CsrfMiddleware().process_view(req, post_form_view, (), {})
+        resp = post_form_response_non_html()
         resp_content = resp.content # needed because process_response modifies resp
         resp2 = CsrfMiddleware().process_response(req, resp)
-        self.assertNotEqual(resp_content, resp2.content)
-        self._check_token_present(resp2)
+        self.assertEquals(resp_content, resp2.content)
 
     def test_process_response_exempt_view(self):
         """
         Check that no post processing is done for an exempt view
         """
-        req = self._get_POST_session_request()
-        resp = csrf_exempt(self.get_view())(req)
+        req = self._get_POST_csrf_cookie_request()
+        resp = csrf_exempt(post_form_view)(req)
         resp_content = resp.content
         resp2 = CsrfMiddleware().process_response(req, resp)
         self.assertEquals(resp_content, resp2.content)
 
     # Check the request processing
-    def test_process_request_no_session(self):
+    def test_process_request_no_session_no_csrf_cookie(self):
         """
-        Check that if no session is present, the middleware does nothing.
-        to the incoming request.
+        Check that if neither a CSRF cookie nor a session cookie are present, the
+        middleware does nothing to the incoming request.
         """
-        req = self._get_POST_no_session_request()
-        req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
+        req = self._get_POST_no_csrf_cookie_request()
+        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
         self.assertEquals(None, req2)
 
-    def test_process_request_session_no_token(self):
+    def test_process_request_csrf_cookie_no_token(self):
         """
-        Check that if a session is present but no token, we get a 'forbidden'
+        Check that if a CSRF cookie is present but no token, we get a 'forbidden'.
         """
-        req = self._get_POST_session_request()
-        req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
+        req = self._get_POST_csrf_cookie_request()
+        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
         self.assertEquals(HttpResponseForbidden, req2.__class__)
 
-    def test_process_request_session_and_token(self):
+    def test_process_request_csrf_cookie_and_token(self):
         """
-        Check that if a session is present and a token, the middleware lets it through
+        Check that if both a cookie and a token is present, the middleware lets it through.
         """
-        req = self._get_POST_session_request_with_token()
-        req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
+        req = self._get_POST_request_with_token()
+        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
         self.assertEquals(None, req2)
 
-    def test_process_request_session_no_token_exempt_view(self):
+    def test_process_request_session_cookie_no_csrf_cookie_token(self):
         """
-        Check that if a session is present and no token, but the csrf_exempt
+        When no CSRF cookie exists, but the user has a session, check that a token
+        using the session cookie as a legacy CSRF cookie is accepted.
+        """
+        orig_secret_key = settings.SECRET_KEY
+        settings.SECRET_KEY = self._secret_key_for_session_test
+        try:
+            req = self._get_POST_session_request_with_token()
+            req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+            self.assertEquals(None, req2)
+        finally:
+            settings.SECRET_KEY = orig_secret_key
+
+    def test_process_request_session_cookie_no_csrf_cookie_no_token(self):
+        """
+        Check that if a session cookie is present but no token and no CSRF cookie,
+        we get a 'forbidden'.
+        """
+        req = self._get_POST_session_request_no_token()
+        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+        self.assertEquals(HttpResponseForbidden, req2.__class__)
+
+    def test_process_request_csrf_cookie_no_token_exempt_view(self):
+        """
+        Check that if a CSRF cookie is present and no token, but the csrf_exempt
         decorator has been applied to the view, the middleware lets it through
         """
-        req = self._get_POST_session_request()
-        req2 = CsrfMiddleware().process_view(req, csrf_exempt(self.get_view()), (), {})
+        req = self._get_POST_csrf_cookie_request()
+        req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {})
         self.assertEquals(None, req2)
 
     def test_ajax_exemption(self):
         """
         Check that AJAX requests are automatically exempted.
         """
-        req = self._get_POST_session_request()
+        req = self._get_POST_csrf_cookie_request()
         req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-        req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
+        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
         self.assertEquals(None, req2)
+
+    # Tests for the template tag method
+    def test_token_node_no_csrf_cookie(self):
+        """
+        Check that CsrfTokenNode works when no CSRF cookie is set
+        """
+        req = self._get_GET_no_csrf_cookie_request()
+        resp = token_view(req)
+        self.assertEquals(u"", resp.content)
+
+    def test_token_node_with_csrf_cookie(self):
+        """
+        Check that CsrfTokenNode works when a CSRF cookie is set
+        """
+        req = self._get_GET_csrf_cookie_request()
+        CsrfViewMiddleware().process_view(req, token_view, (), {})
+        resp = token_view(req)
+        self._check_token_present(resp)
+
+    def test_token_node_with_new_csrf_cookie(self):
+        """
+        Check that CsrfTokenNode works when a CSRF cookie is created by
+        the middleware (when one was not already present)
+        """
+        req = self._get_GET_no_csrf_cookie_request()
+        CsrfViewMiddleware().process_view(req, token_view, (), {})
+        resp = token_view(req)
+        resp2 = CsrfViewMiddleware().process_response(req, resp)
+        csrf_cookie = resp2.cookies[_cookie_name()]
+        self._check_token_present(resp, csrf_id=csrf_cookie.value)
Index: django/contrib/csrf/templatetags/csrf.py
===================================================================
--- django/contrib/csrf/templatetags/csrf.py	(revision 0)
+++ django/contrib/csrf/templatetags/csrf.py	(revision 0)
@@ -0,0 +1,25 @@
+from django import template
+from django.utils.safestring import mark_safe
+
+register = template.Library()
+
+class CsrfTokenNode(template.Node):
+    def render(self, context):
+        csrf_token = context.get('csrf_token', None)
+        if csrf_token:
+            if csrf_token == 'NOTNEEDED':
+                return mark_safe(u"")
+            else:
+                return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % (csrf_token))
+        else:
+            # It's very probable that the token is missing because of
+            # misconfiguration, so we raise a warning
+            from django.conf import settings
+            if settings.DEBUG:
+                import warnings
+                warnings.warn("A {% csrf_token %} was used in a template, but the context did not provide the value.  This is usually caused by not having 'django.contrib.csrf.context_processors.csrf' in TEMPLATE_CONTEXT_PROCESSORS.")
+            return u''
+
+def csrf_token(parser, token):
+    return CsrfTokenNode()
+register.tag(csrf_token)

Property changes on: django/contrib/csrf/templatetags/csrf.py
___________________________________________________________________
Added: svn:eol-style
   + native

Index: django/contrib/csrf/templatetags/__init__.py
===================================================================
--- django/contrib/csrf/templatetags/__init__.py	(revision 0)
+++ django/contrib/csrf/templatetags/__init__.py	(revision 0)
@@ -0,0 +1 @@
+

Property changes on: django/contrib/csrf/templatetags/__init__.py
___________________________________________________________________
Added: svn:eol-style
   + native

Index: django/contrib/csrf/context_processors.py
===================================================================
--- django/contrib/csrf/context_processors.py	(revision 0)
+++ django/contrib/csrf/context_processors.py	(revision 0)
@@ -0,0 +1,20 @@
+from django.contrib.csrf.middleware import get_token
+from django.utils.functional import memoize
+
+def csrf(request):
+    """
+    Context processor that provides a CSRF token, or the string
+    'NOTNEEDED' if it is not needed
+    """
+    def _get_val():
+        token = get_token(request)
+        if token is None:
+            # In order to be able to provide debugging info in the
+            # case of misconfiguration, we use a sentinel value
+            # instead of returning an empty dict.
+            return 'NOTNEEDED'
+        else:
+            return token
+    _get_val = memoize(_get_val, {}, 0)
+
+    return {'csrf_token': _get_val() }

Property changes on: django/contrib/csrf/context_processors.py
___________________________________________________________________
Added: svn:eol-style
   + native

Index: tests/regressiontests/admin_views/tests.py
===================================================================
--- tests/regressiontests/admin_views/tests.py	(revision 10618)
+++ tests/regressiontests/admin_views/tests.py	(working copy)
@@ -811,8 +811,9 @@
         # 4 action inputs (3 regular checkboxes, 1 checkbox to select all)
         # main form submit button = 1
         # search field and search submit button = 2
-        # 6 + 2 + 1 + 2 = 11 inputs
-        self.failUnlessEqual(response.content.count("<input"), 15)
+        # csrf hidden field = 1
+        # 6 + 2 + 4 + 1 + 2 + 1 = 16 inputs
+        self.failUnlessEqual(response.content.count("<input"), 16)
         # 1 select per object = 3 selects
         self.failUnlessEqual(response.content.count("<select"), 4)
 
Index: tests/runtests.py
===================================================================
--- tests/runtests.py	(revision 10618)
+++ tests/runtests.py	(working copy)
@@ -30,6 +30,7 @@
     'django.contrib.sessions',
     'django.contrib.comments',
     'django.contrib.admin',
+    'django.contrib.csrf',
 ]
 
 def get_test_models():
Index: AUTHORS
===================================================================
--- AUTHORS	(revision 10618)
+++ AUTHORS	(working copy)
@@ -463,6 +463,8 @@
     Gasper Zejn <zejn@kiberpipa.org>
     Jarek Zgoda <jarek.zgoda@gmail.com>
     Cheng Zhang
+    Glenn
+    bthomas
 
 A big THANK YOU goes to:
 
Index: docs/topics/http/middleware.txt
===================================================================
--- docs/topics/http/middleware.txt	(revision 10618)
+++ docs/topics/http/middleware.txt	(working copy)
@@ -29,6 +29,7 @@
     MIDDLEWARE_CLASSES = (
         'django.middleware.common.CommonMiddleware',
         'django.contrib.sessions.middleware.SessionMiddleware',
+        'django.contrib.csrf.middleware.CsrfViewMiddleware',
         'django.contrib.auth.middleware.AuthenticationMiddleware',
     )
 
Index: docs/ref/contrib/csrf.txt
===================================================================
--- docs/ref/contrib/csrf.txt	(revision 10618)
+++ docs/ref/contrib/csrf.txt	(working copy)
@@ -7,46 +7,161 @@
 .. module:: django.contrib.csrf
    :synopsis: Protects against Cross Site Request Forgeries
 
-The CsrfMiddleware class provides easy-to-use protection against
+The CSRF middleware and template tag provides easy-to-use protection against
 `Cross Site Request Forgeries`_.  This type of attack occurs when a malicious
 Web site creates a link or form button that is intended to perform some action
-on your Web site, using the credentials of a logged-in user who is tricked
-into clicking on the link in their browser.
+on your Web site, using the credentials of a logged-in user who is tricked into
+clicking on the link in their browser.
 
-The first defense against CSRF attacks is to ensure that GET requests
-are side-effect free.  POST requests can then be protected by adding this
-middleware into your list of installed middleware.
+The first defense against CSRF attacks is to ensure that GET requests are
+side-effect free.  POST requests can then be protected by adding these
+middleware into your list of installed middleware following the steps below.
 
+.. versionadded:: 1.1
+    The 'contrib' apps, including the admin, depend on the functionality
+    described here. Anyone upgrading from earlier versions should read
+    the `Upgrading notes`_ carefully.
+
 .. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
 
 How to use it
 =============
 
-Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
-your list of middleware classes, :setting:`MIDDLEWARE_CLASSES`. It needs to process
-the response after the SessionMiddleware, so must come before it in the
-list. It also must process the response before things like compression
-happen to the response, so it must come after GZipMiddleware in the
-list.
+.. versionchanged:: 1.1
+    The template tag functionality (the recommended way to use this) was added
+    in version 1.1. The previous method (still available) is described under
+    `Legacy method`_.
 
-The ``CsrfMiddleware`` class is actually composed of two middleware:
-``CsrfViewMiddleware`` which performs the checks on incoming requests,
-and ``CsrfResponseMiddleware`` which performs post-processing of the
-result.  This allows the individual components to be used and/or
-replaced instead of using ``CsrfMiddleware``.
+To enable CSRF protection for your views, follow these steps:
 
-.. versionchanged:: 1.1
-    (previous versions of Django did not provide these two components
-    of ``CsrfMiddleware`` as described above)
+    1. Add the middleware
+       ``'django.contrib.csrf.middleware.CsrfViewMiddleware'`` to your list of
+       middleware classes, :setting:`MIDDLEWARE_CLASSES`.  (Currently it can
+       come anywhere in the list with respect to other middleware included in
+       Django.  It should come before any view middleware that assume that CSRF
+       attacks have been dealt with.)
 
+    2. Add ``'django.contrib.csrf'`` to your :setting:`INSTALLED_APPS`.
+
+    3. In any template that uses a POST form, first load the 'csrf' template tag
+       library::
+
+           {% load csrf %}
+
+       Then use the ``csrf_token`` tag inside the ``<form>`` element, e.g.::
+
+           <form action="" method="POST">{% csrf_token %}
+
+    4. In the corresponding view functions, ensure that the
+       ``'django.contrib.csrf.context_processors.csrf'`` is being used. Usually,
+       this can be done in one of two ways:
+
+       1. Using RequestContext:
+
+          1. Ensure ``'django.contrib.csrf.context_processors.csrf'`` is present
+             in your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting. It is
+             present by default.
+
+          2. Use ``RequestContext`` as the context instance in the relevant
+             views. If you are using generic views or contrib apps, you are
+             covered already.
+
+       2. Manually import and use the processor to generate the CSRF token and
+          add it to the template context. e.g.::
+
+              from django.contrib.csrf.context_processors import csrf
+              from django.template import Context
+              from django.shortcuts import render_to_response
+              def my_view(request):
+                  c = Context()
+                  c.update(csrf(request))
+                  return render_to_response("a_template.html", context_instance=c)
+
+Legacy method
+-------------
+
+In Django 1.0, the template tag did not exist.  Instead, a post-processing
+middleware that re-wrote POST forms to include the CRSF token was used.  If you
+are upgrading a site from version 1.0 or earlier, please read this section and
+the `Upgrading notes`_ below.  The post-processing middleware is still available
+as ``CsrfResponseMiddleware``, and it can be used by following these steps:
+
+    1. Follow step 1 above to install ``CsrfViewMiddleware``.
+
+    2. Add ``'django.contrib.csrf.middleware.CsrfResponseMiddleware'`` to your
+       :setting:`MIDDLEWARE_CLASSES` setting.
+
+       ``CsrfResponseMiddleware`` needs to process the response before things
+       like compression happen to the response, so it must come after
+       ``GZipMiddleware`` in the list.
+
+Use of the ``CsrfResponseMiddleware`` is not recommended, but it can be used as
+an interim measure until applications have been updated to use the ``{%
+crsf_token %}`` tag.
+
+Django 1.0 provided a single ``CsrfMiddleware`` class.  This is also still
+available for backwards compatibility.  It combines the functions of the two new
+middleware.
+
+Note also that previous versions of these classes depended on the sessions
+framework, but this dependency has now been removed, with backward compatibility
+support so that upgrading will not produce any issues.
+
+Upgrading notes
+---------------
+
+When upgrading to version 1.1 or later, you may have applications that rely on
+the old post-processing functionality for CSRF protection, or you may not have
+enabled any CSRF protection.  This section outlines the steps necessary for a
+smooth upgrade, without having to fix all the applications to use the new
+template tag method immediately.
+
+If you are using any of the contrib apps (such as the admin), there are some
+required steps for these applications to continue working. First, the CSRF
+application must be added to :setting:`INSTALLED_APPS` (See `How to use it`_
+above, step 2).  Second, the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting must
+be updated (step 4.1.1 above).
+
+If you have ``CsrfMiddleware`` in your :setting:`MIDDLEWARE_CLASSES`, you will now
+have a working installation with CSRF protection.  It is recommended at this
+point that you replace ``CsrfMiddleware`` with its two components,
+``CsrfViewMiddleware`` and ``CsrfResponseMiddleware``.
+
+If you do not have any of the middleware in your :setting:`MIDDLEWARE_CLASSES`,
+you will have a working installation but without any CSRF protection (just as
+you had before). It is recommended to install ``CsrfViewMiddleware`` and
+``CsrfResponseMiddleware``, but if you are not interested in having any CSRF
+protection, you can simply stop here.
+
+Assuming you have followed the above, all views in your Django site will now be
+protected by the ``CsrfViewMiddleware``.  Contrib apps meet the requirements
+imposed by the ``CsrfViewMiddleware`` using the template tag, and other
+applications in your project will meet its requirements by virtue of the
+``CsrfResponseMiddleware``.
+
+The next step is to update all your applications to use the template tag, as
+described in `How to use it`_, steps 3-4.  This can be done as soon as is
+practical. Any applications that are updated will now require Django 1.1 or
+later, since they will use the CSRF template tag library which was not available
+in earlier versions.
+
+Finally, once all applications are upgraded, ``CsrfResponseMiddleware`` can be
+removed from your settings.
+
+While ``CsrfResponseMiddleware`` is still in use, the ``csrf_response_exempt``
+decorator, described in `Exceptions`_, may be useful.  The post-processing
+middleware imposes a performance hit, and any views that have been upgraded to
+use the new template tag method no longer need it.  Using this decorator will
+allow you to avoid this performance hit.
+
 Exceptions
 ----------
 
 .. versionadded:: 1.1
 
-To manually exclude a view function from being handled by the
-CsrfMiddleware, you can use the ``csrf_exempt`` decorator, found in
-the ``django.contrib.csrf.middleware`` module. For example::
+To manually exclude a view function from being handled by either of the two CSRF
+middleware, you can use the ``csrf_exempt`` decorator, found in the
+``django.contrib.csrf.middleware`` module. For example::
 
     from django.contrib.csrf.middleware import csrf_exempt
 
@@ -54,49 +169,69 @@
         return HttpResponse('Hello world')
     my_view = csrf_exempt(my_view)
 
-Like the middleware itself, the ``csrf_exempt`` decorator is composed
-of two parts: a ``csrf_view_exempt`` decorator and a
-``csrf_response_exempt`` decorator, found in the same module.  These
-disable the view protection mechanism (``CsrfViewMiddleware``) and the
-response post-processing (``CsrfResponseMiddleware``) respectively.
-They can be used individually if required.
+Like the middleware, the ``csrf_exempt`` decorator is composed of two parts: a
+``csrf_view_exempt`` decorator and a ``csrf_response_exempt`` decorator, found
+in the same module.  These disable the view protection mechanism
+(``CsrfViewMiddleware``) and the response post-processing
+(``CsrfResponseMiddleware``) respectively.  They can be used individually if
+required.
 
-You don't have to worry about doing this for most AJAX views. Any
-request sent with "X-Requested-With: XMLHttpRequest" is automatically
-exempt. (See the next section.)
+You don't have to worry about doing this for most AJAX views. Any request sent
+with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the next
+section.)
 
+Rejected requests
+=================
+
+By default, a '403 Forbidden' response is sent to the user if an incoming
+request fails the checks performed by ``CsrfViewMiddleware``.  This should
+usually only be seen when there is a genuine Cross Site Request Forgery, or
+when, due to a programming error, the CSRF token has not been included with a
+POST form.
+
+No logging is done, and the error message is not very friendly, so you may want
+to provide your own page for handling this condition.  To do this, simply set
+the :setting:`CSRF_FAILURE_VIEW` setting to a dotted path to your own view
+function.
+
 How it works
 ============
 
-CsrfMiddleware does two things:
+The CSRF protection is based on three things:
 
-1. It modifies outgoing requests by adding a hidden form field to all
-   'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is
-   a hash of the session ID plus a secret. If there is no session ID set,
-   this modification of the response isn't done, so there is very little
-   performance penalty for those requests that don't have a session.
-   (This is done by ``CsrfResponseMiddleware``).
+1. A CSRF cookie that is set to a random value, which other sites will not have
+   access to.
 
-2. On all incoming POST requests that have the session cookie set, it
-   checks that the 'csrfmiddlewaretoken' is present and correct. If it
-   isn't, the user will get a 403 error. (This is done by
-   ``CsrfViewMiddleware``)
+   This cookie is set by ``CsrfViewMiddleware``.
 
-This ensures that only forms that have originated from your Web site
-can be used to POST data back.
+2. A hidden form field with the name 'csrfmiddlewaretoken' present in all
+   outgoing POST forms.  The value of this field is a hash of the CSRF cookie
+   plus a site-specific secret.
 
+   This part is done by the template tag (and with the legacy method, it is done
+   by ``CsrfResponseMiddleware``).
+
+3. For all incoming POST requests that have the CSRF cookie set, the
+   'csrfmiddlewaretoken' must be present and correct. If it isn't, the user will
+   get a 403 error.
+
+   This check is done by ``CsrfViewMiddleware``.
+
+This ensures that only forms that have originated from your Web site can be used
+to POST data back.
+
 It deliberately only targets HTTP POST requests (and the corresponding POST
-forms). GET requests ought never to have any potentially dangerous side
-effects (see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a
-CSRF attack with a GET request ought to be harmless.
+forms). GET requests ought never to have any potentially dangerous side effects
+(see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a CSRF attack with a GET
+request ought to be harmless.
 
-POST requests that are not accompanied by a session cookie are not protected,
-but they do not need to be protected, since the 'attacking' Web site
-could make these kind of requests anyway.
+If the user has never visited the site before, and their browser makes a POST
+request to the site, there will be no CSRF cookie, and no CSRF check will be
+done. However, this is harmless, since if the user has never visited the site
+before, they will not have any authentication that could be abused.
 
-The Content-Type is checked before modifying the response, and only
-pages that are served as 'text/html' or 'application/xml+xhtml'
-are modified.
+The Content-Type is checked before modifying the response, and only pages that
+are served as 'text/html' or 'application/xml+xhtml' are modified.
 
 The middleware tries to be smart about requests that come in via AJAX. Many
 JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP header;
@@ -112,13 +247,10 @@
 Limitations
 ===========
 
-CsrfMiddleware requires Django's session framework to work. If you have
-a custom authentication system that manually sets cookies and the like,
-it won't help you.
-
-If your app creates HTML pages and forms in some unusual way, (e.g.
-it sends fragments of HTML in JavaScript document.write statements)
-you might bypass the filter that adds the hidden field to the form,
-in which case form submission will always fail.  It may still be possible
-to use the middleware, provided you can find some way to get the
-CSRF token and ensure that is included when your form is submitted.
+If you are using ``CsrfResponseMiddleware`` and your app creates HTML pages and
+forms in some unusual way, (e.g.  it sends fragments of HTML in JavaScript
+document.write statements) you might bypass the filter that adds the hidden
+field to the form, in which case form submission will always fail.  It may still
+be possible to use this middleware, provided you use the template tag or
+:meth:`django.contrib.csrf.middleware.get_token` to get the CSRF token and
+ensure that is included when your form is submitted.
Index: docs/ref/settings.txt
===================================================================
--- docs/ref/settings.txt	(revision 10618)
+++ docs/ref/settings.txt	(working copy)
@@ -146,6 +146,14 @@
 
 .. setting:: DATABASE_ENGINE
 
+CSRF_FAILURE_VIEW
+-----------------
+
+Default: ``'django.contrib.csrf.views.csrf_failure'``
+
+The name of the view function to be used when an incoming request
+is rejected by the CSRF protection. See :ref:`ref-contrib-csrf`.
+
 DATABASE_ENGINE
 ---------------
 
@@ -762,6 +770,7 @@
 
     ('django.middleware.common.CommonMiddleware',
      'django.contrib.sessions.middleware.SessionMiddleware',
+     'django.contrib.csrf.middleware.CsrfViewMiddleware',
      'django.contrib.auth.middleware.AuthenticationMiddleware',)
 
 A tuple of middleware classes to use. See :ref:`topics-http-middleware`.
