diff -r 1b4df7524b7c AUTHORS
--- a/AUTHORS	Mon Aug 24 15:45:48 2009 +0000
+++ b/AUTHORS	Mon Aug 31 17:40:40 2009 +0100
@@ -468,6 +468,8 @@
     Gasper Zejn <zejn@kiberpipa.org>
     Jarek Zgoda <jarek.zgoda@gmail.com>
     Cheng Zhang
+    Glenn
+    bthomas
 
 A big THANK YOU goes to:
 
diff -r 1b4df7524b7c django/conf/global_settings.py
--- a/django/conf/global_settings.py	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/conf/global_settings.py	Mon Aug 31 17:40:40 2009 +0100
@@ -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',
 )
 
@@ -300,6 +301,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',
@@ -374,6 +376,18 @@
 # 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'
+
+# Name and domain for CSRF cookie.
+CSRF_COOKIE_NAME = 'csrftoken'
+CSRF_COOKIE_DOMAIN = None
+
 ###########
 # TESTING #
 ###########
diff -r 1b4df7524b7c django/conf/project_template/settings.py
--- a/django/conf/project_template/settings.py	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/conf/project_template/settings.py	Mon Aug 31 17:40:40 2009 +0100
@@ -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',
 )
diff -r 1b4df7524b7c django/contrib/admin/sites.py
--- a/django/contrib/admin/sites.py	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/sites.py	Mon Aug 31 17:40:40 2009 +0100
@@ -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.core.urlresolvers import reverse
@@ -154,8 +155,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, cacheable=False):
         """
@@ -186,6 +191,7 @@
             return view(request, *args, **kwargs)
         if not cacheable:
             inner = never_cache(inner)
+        inner = csrf_response_exempt(inner)
         return update_wrapper(inner, view)
 
     def get_urls(self):
diff -r 1b4df7524b7c django/contrib/admin/templates/admin/auth/user/change_password.html
--- a/django/contrib/admin/templates/admin/auth/user/change_password.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/admin/auth/user/change_password.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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 %}
diff -r 1b4df7524b7c django/contrib/admin/templates/admin/change_form.html
--- a/django/contrib/admin/templates/admin/change_form.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/admin/change_form.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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 %}
diff -r 1b4df7524b7c django/contrib/admin/templates/admin/change_list.html
--- a/django/contrib/admin/templates/admin/change_list.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/admin/change_list.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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 %}
diff -r 1b4df7524b7c django/contrib/admin/templates/admin/delete_confirmation.html
--- a/django/contrib/admin/templates/admin/delete_confirmation.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/admin/delete_confirmation.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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" %}" />
diff -r 1b4df7524b7c django/contrib/admin/templates/admin/delete_selected_confirmation.html
--- a/django/contrib/admin/templates/admin/delete_selected_confirmation.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/admin/delete_selected_confirmation.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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 }}" />
diff -r 1b4df7524b7c django/contrib/admin/templates/admin/login.html
--- a/django/contrib/admin/templates/admin/login.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/admin/login.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/admin/templates/admin/template_validator.html
--- a/django/contrib/admin/templates/admin/template_validator.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/admin/template_validator.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/admin/templates/registration/password_change_form.html
--- a/django/contrib/admin/templates/registration/password_change_form.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/registration/password_change_form.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/admin/templates/registration/password_reset_confirm.html
--- a/django/contrib/admin/templates/registration/password_reset_confirm.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/registration/password_reset_confirm.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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 }}
diff -r 1b4df7524b7c django/contrib/admin/templates/registration/password_reset_form.html
--- a/django/contrib/admin/templates/registration/password_reset_form.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/admin/templates/registration/password_reset_form.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/comments/templates/comments/approve.html
--- a/django/contrib/comments/templates/comments/approve.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/comments/templates/comments/approve.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/comments/templates/comments/delete.html
--- a/django/contrib/comments/templates/comments/delete.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/comments/templates/comments/delete.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/comments/templates/comments/flag.html
--- a/django/contrib/comments/templates/comments/flag.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/comments/templates/comments/flag.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/comments/templates/comments/form.html
--- a/django/contrib/comments/templates/comments/form.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/comments/templates/comments/form.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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 %}
diff -r 1b4df7524b7c django/contrib/comments/templates/comments/moderation_queue.html
--- a/django/contrib/comments/templates/comments/moderation_queue.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/comments/templates/comments/moderation_queue.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/comments/templates/comments/preview.html
--- a/django/contrib/comments/templates/comments/preview.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/comments/templates/comments/preview.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/csrf/context_processors.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/contrib/csrf/context_processors.py	Mon Aug 31 17:40:40 2009 +0100
@@ -0,0 +1,20 @@
+from django.contrib.csrf.middleware import get_token
+from django.utils.functional import lazy
+
+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 = lazy(_get_val, str)
+
+    return {'csrf_token': _get_val() }
diff -r 1b4df7524b7c django/contrib/csrf/middleware.py
--- a/django/contrib/csrf/middleware.py	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/csrf/middleware.py	Mon Aug 31 17:40:40 2009 +0100
@@ -5,94 +5,162 @@
 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):
+# 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_failure_view():
+    """
+    Returns the view to be used for CSRF rejections
+    """
+    return get_callable(settings.CSRF_FAILURE_VIEW)
+
+def _get_new_csrf_key():
+    return md5_constructor("%s%s"
+                % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
+
+def _make_legacy_session_token(session_id):
     return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
 
+def get_token(request):
+    """
+    Returns the the CSRF token required for a POST form.
+    """
+    request.META["CSRF_COOKIE_USED"] = True
+    return request.META.get("CSRF_COOKIE", None)
+
 class CsrfViewMiddleware(object):
     """
     Middleware that requires a present and correct csrfmiddlewaretoken
-    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):
+        reject = lambda: _get_failure_view()(request)
+
+        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[settings.CSRF_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 request.is_secure():
+                # Strict referer checking for HTTPS
+                referer = request.META.get('HTTP_REFERER')
+                if referer is None:
+                    return reject()
 
-            csrf_token = _make_token(session_id)
+                # The following check ensures that the referer is HTTPS,
+                # the domains match and the ports match.  This might be too strict.
+                if not referer.startswith('https://%s/' % request.get_host()):
+                    return reject()
+
+            # 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. For POST requests,
+                    # we insist on a CSRF cookie, and in this way we can avoid
+                    # all CSRF attacks, including login CSRF.
+                    return reject()
+            else:
+                csrf_token = request.META["CSRF_COOKIE"]
+
             # 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 reject()
 
         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
+
+        if not request.META.get("CSRF_COOKIE_USED", False):
+            return response
+
+        # Set the CSRF cookie even if it's already set, so we renew the expiry timer.
+        response.set_cookie(settings.CSRF_COOKIE_NAME,
+                request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
+                domain=settings.CSRF_COOKIE_DOMAIN)
+
+        return response
+
 class CsrfResponseMiddleware(object):
     """
-    Middleware that post-processes a response to add a
-    csrfmiddlewaretoken if the response/request have an active
-    session.
+    DEPRECATED
+    Middleware that post-processes a response to add a csrfmiddlewaretoken.
+
+    This exists for backwards compatibility and as an interim measure until
+    applications are converted to using use the csrf_token template tag
+    instead. It will be removed in Django 1.4.
     """
+    def __init__(self):
+        import warnings
+        warnings.warn(
+            "CsrfResponseMiddleware and CsrfMiddleware are deprecated; use CsrfViewMiddleware and the template tag instead (see CSRF documentation).",
+            PendingDeprecationWarning
+        )
+
     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 csrf_token is not None and \
-                response['Content-Type'].split(';')[0] in _HTML_TYPES:
+        if response['Content-Type'].split(';')[0] in _HTML_TYPES:
+            csrf_token = get_token(request)
+            # If csrf_token is None, we have no token for this request, which probably
+            # means that this is a response from a request middleware.
+            if csrf_token is None:
+                return response
 
             # 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,27 +172,33 @@
             response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
         return response
 
-class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware):
-    """Django middleware that adds protection against Cross Site
+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 response post-processing first, because that calls
+        # get_token(), which triggers a flag saying that the CSRF cookie needs
+        # to be sent (done in CsrfViewMiddleware.process_response)
+        resp2 = self.response_middleware.process_response(request, resp)
+        return self.view_middleware.process_response(request, resp2)
 
-    If you are setting cookies directly, instead of using Django's
-    session framework, this middleware will not work.
-
-    CsrfMiddleWare is composed of two middleware, CsrfViewMiddleware
-    and CsrfResponseMiddleware which can be used independently.
-    """
-    pass
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        return self.view_middleware.process_view(request, callback, callback_args,
+                                                 callback_kwargs)
 
 def csrf_response_exempt(view_func):
     """
diff -r 1b4df7524b7c django/contrib/csrf/templatetags/__init__.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/contrib/csrf/templatetags/__init__.py	Mon Aug 31 17:40:40 2009 +0100
@@ -0,0 +1,1 @@
+
diff -r 1b4df7524b7c django/contrib/csrf/templatetags/csrf.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/contrib/csrf/templatetags/csrf.py	Mon Aug 31 17:40:40 2009 +0100
@@ -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 or not using RequestContext.")
+            return u''
+
+def csrf_token(parser, token):
+    return CsrfTokenNode()
+register.tag(csrf_token)
diff -r 1b4df7524b7c django/contrib/csrf/tests.py
--- a/django/contrib/csrf/tests.py	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/csrf/tests.py	Mon Aug 31 17:40:40 2009 +0100
@@ -1,144 +1,321 @@
 # -*- coding: utf-8 -*-
 
 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.http import HttpRequest, HttpResponse
+from django.contrib.csrf.middleware import CsrfMiddleware, CsrfViewMiddleware, 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):
+    """A view that returns a POST form (without a token)"""
     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):
+    """A view that uses {% csrf_token %}"""
+    return HttpResponse(_render_csrf_token_template(request))
+
+def non_token_view_using_request_processor(request):
+    """
+    A view that doesn't use the token, but does use the csrf view processor.
+    """
+    context = RequestContext(request, processors=[csrf])
+    template = Template("{% load csrf %}")
+    return HttpResponse(template.render(context))
+
+class TestingHttpRequest(HttpRequest):
+    """
+    A version of HttpRequest that allows us to change some things
+    more easily
+    """
+    def is_secure(self):
+        return getattr(self, '_is_secure', False)
+
 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):
-        return HttpRequest()
+    def _get_GET_no_csrf_cookie_request(self):
+        return TestingHttpRequest()
 
-    def _get_GET_session_request(self):
-        req = self._get_GET_no_session_request()
+    def _get_GET_csrf_cookie_request(self):
+        req = TestingHttpRequest()
+        req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id
+        return req
+
+    def _get_POST_csrf_cookie_request(self):
+        req = self._get_GET_csrf_cookie_request()
+        req.method = "POST"
+        return req
+
+    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'] = self._csrf_id
+        return req
+
+    def _get_POST_session_request_with_token(self):
+        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_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_POST_session_request(self):
-        req = self._get_GET_session_request()
-        req.method = "POST"
-        return req
+    def _check_token_present(self, response, csrf_id=None):
+        self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
 
-    def _get_POST_no_session_request(self):
-        req = self._get_GET_no_session_request()
-        req.method = "POST"
-        return req
+    # Check the post processing and outgoing cookie
+    def test_process_response_no_csrf_cookie(self):
+        """
+        When no prior CSRF cookie exists, check that the cookie is created and a
+        token is inserted.
+        """
+        req = self._get_GET_no_csrf_cookie_request()
+        CsrfMiddleware().process_view(req, post_form_view, (), {})
 
-    def _get_POST_session_request_with_token(self):
-        req = self._get_POST_session_request()
-        req.POST['csrfmiddlewaretoken'] = _make_token(self._session_id)
-        return req
-
-    def _get_post_form_response(self):
-        return post_form_response()
-
-    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):
-        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-processor does nothing if no session active
-        """
-        req = self._get_GET_no_session_request()
-        resp = self._get_post_form_response()
+        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(settings.CSRF_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_get_token_used(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()
+        # This is checking that CsrfViewMiddleware has the cookie setting
+        # code. Most of the other tests use CsrfMiddleware.
+        req = self._get_GET_no_csrf_cookie_request()
+        # token_view calls get_token() indirectly
+        CsrfViewMiddleware().process_view(req, token_view, (), {})
+        resp = token_view(req)
+        resp2 = CsrfViewMiddleware().process_response(req, resp)
+
+        csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
+        self.assertNotEqual(csrf_cookie, False)
+
+    def test_process_response_get_token_not_used(self):
+        """
+        Check that if get_token() is not called, the view middleware does not
+        add a cookie.
+        """
+        # This is important to make pages cacheable.  Pages which do call
+        # get_token(), assuming they use the token, are not cacheable because
+        # the token is specific to the user
+        req = self._get_GET_no_csrf_cookie_request()
+        # non_token_view_using_request_processor does not call get_token(), but
+        # does use the csrf request processor.  By using this, we are testing
+        # that the view processor is properly lazy and doesn't call get_token()
+        # until needed.
+        CsrfViewMiddleware().process_view(req, non_token_view_using_request_processor, (), {})
+        resp = non_token_view_using_request_processor(req)
+        resp2 = CsrfViewMiddleware().process_response(req, resp)
+
+        csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
+        self.assertEqual(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 rejects the incoming request.  This will stop login CSRF.
         """
-        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(403, req2.status_code)
+
+    def test_process_request_csrf_cookie_no_token(self):
+        """
+        Check that if a CSRF cookie is present but no token, the middleware
+        rejects the incoming request.
+        """
+        req = self._get_POST_csrf_cookie_request()
+        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+        self.assertEquals(403, req2.status_code)
+
+    def test_process_request_csrf_cookie_and_token(self):
+        """
+        Check that if both a cookie and a token is present, the middleware lets it through.
+        """
+        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(self):
+    def test_process_request_session_cookie_no_csrf_cookie_token(self):
         """
-        Check that if a session is present but no token, we get a 'forbidden'
+        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.
         """
-        req = self._get_POST_session_request()
-        req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
-        self.assertEquals(HttpResponseForbidden, req2.__class__)
+        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_and_token(self):
+    def test_process_request_session_cookie_no_csrf_cookie_no_token(self):
         """
-        Check that if a session is present and a token, the middleware lets it through
+        Check that if a session cookie is present but no token and no CSRF cookie,
+        the request is rejected.
         """
-        req = self._get_POST_session_request_with_token()
-        req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})
-        self.assertEquals(None, req2)
+        req = self._get_POST_session_request_no_token()
+        req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+        self.assertEquals(403, req2.status_code)
 
-    def test_process_request_session_no_token_exempt_view(self):
+    def test_process_request_csrf_cookie_no_token_exempt_view(self):
         """
-        Check that if a session is present and no token, but the csrf_exempt
+        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[settings.CSRF_COOKIE_NAME]
+        self._check_token_present(resp, csrf_id=csrf_cookie.value)
+
+    def test_response_middleware_without_view_middleware(self):
+        """
+        Check that CsrfResponseMiddleware finishes without error if the view middleware
+        has not been called, as is the case if a request middleware returns a response.
+        """
+        req = self._get_GET_no_csrf_cookie_request()
+        resp = post_form_view(req)
+        CsrfMiddleware().process_response(req, resp)
+
+    def test_https_bad_referer(self):
+        """
+        Test that a POST HTTP request with a bad referer is rejected
+        """
+        req = self._get_POST_request_with_token()
+        req._is_secure = True
+        req.META['HTTP_HOST'] = 'www.example.com'
+        req.META['HTTP_REFERER'] = 'https://www.evil.org/somepage'
+        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+        self.assertNotEqual(None, req2)
+        self.assertEquals(403, req2.status_code)
+
+    def test_https_good_referer(self):
+        """
+        Test that a POST HTTP request with a good referer is accepted
+        """
+        req = self._get_POST_request_with_token()
+        req._is_secure = True
+        req.META['HTTP_HOST'] = 'www.example.com'
+        req.META['HTTP_REFERER'] = 'https://www.example.com/somepage'
+        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+        self.assertEquals(None, req2)
diff -r 1b4df7524b7c django/contrib/csrf/views.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/contrib/csrf/views.py	Mon Aug 31 17:40:40 2009 +0100
@@ -0,0 +1,60 @@
+from django.http import HttpResponseForbidden
+from django.template import Context, Template
+from django.conf import settings
+
+# We include the template here, since one possible error in setting up CSRF is
+# that the developer hasn't included the CSRF app in INSTALLED_APPS, in which
+# case we won't be able to find the template.
+CSRF_FAILRE_TEMPLATE = """
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>403 Forbidden</title>
+</head>
+<body>
+  <h1>403 Forbidden</h1>
+  <p>CSRF verification failed. Request aborted.</p>
+  {% if DEBUG %}
+  <h2>Help</h2>
+
+  <p>This can occur when there is a genuine Cross Site Request Forgery, or when
+  <a
+  href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-csrf'>Django's
+  CSRF mechanism</a> has not been used correctly.  For POST forms, you need to
+  ensure:</p>
+
+  <ul>
+    <li>The view function uses <a
+    href='http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext'><tt>RequestContext</tt></a>
+    for the template, instead of <tt>Context</tt>.</li>
+
+    <li>In the template, there is a <tt>{% templatetag openblock %} load csrf
+    {% templatetag closeblock %}</tt> command to load the the CSRF template
+    tag.</li>
+
+    <li>In the template, there is a <tt>{% templatetag openblock %} csrf_token
+    {% templatetag closeblock %}</tt> template tag inside each POST form that
+    targets an internal URL.</li>
+
+    <li>You have <tt>'django.contrib.csrf'</tt> in your INSTALLED_APPS setting.</li>
+
+    <li>You have <tt>'django.contrib.csrf.context_processors.csrf'</tt> in your TEMPLATE_CONTEXT_PROCESSORS setting.</li>
+  </ul>
+
+  <p>You're seeing the help section of this page because you have <code>DEBUG =
+  True</code> in your Django settings file. Change that to <code>False</code>,
+  and only the initial error message will be displayed.  </p>
+
+  {% endif %}
+</body>
+</html>
+"""
+
+def csrf_failure(request):
+    """
+    Default view used when request fails CSRF protection
+    """
+    t = Template(CSRF_FAILRE_TEMPLATE)
+    c = Context({'DEBUG': settings.DEBUG})
+    return HttpResponseForbidden(t.render(c), mimetype='text/html')
diff -r 1b4df7524b7c django/contrib/formtools/templates/formtools/form.html
--- a/django/contrib/formtools/templates/formtools/form.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/formtools/templates/formtools/form.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c django/contrib/formtools/templates/formtools/preview.html
--- a/django/contrib/formtools/templates/formtools/preview.html	Mon Aug 24 15:45:48 2009 +0000
+++ b/django/contrib/formtools/templates/formtools/preview.html	Mon Aug 31 17:40:40 2009 +0100
@@ -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>
diff -r 1b4df7524b7c docs/intro/tutorial01.txt
--- a/docs/intro/tutorial01.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/intro/tutorial01.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -209,6 +209,12 @@
 
     * :mod:`django.contrib.contenttypes` -- A framework for content types.
 
+    * :mod:`django.contrib.csrf` -- A security system for dealing with Cross
+      Site Request Forgeries
+
+    * :mod:`django.contrib.csrf` -- An easy-to-use system for protecting
+      against Cross Site Request Forgeries.
+
     * :mod:`django.contrib.sessions` -- A session framework.
 
     * :mod:`django.contrib.sites` -- A framework for managing multiple sites
@@ -216,7 +222,7 @@
 
 These applications are included by default as a convenience for the common case.
 
-Each of these applications makes use of at least one database table, though,
+Most of these applications makes use of at least one database table, though,
 so we need to create the tables in the database before we can use them. To do
 that, run the following command:
 
@@ -386,6 +392,7 @@
     INSTALLED_APPS = (
         'django.contrib.auth',
         'django.contrib.contenttypes',
+        'django.contrib.csrf',
         'django.contrib.sessions',
         'django.contrib.sites',
         'mysite.polls'
diff -r 1b4df7524b7c docs/intro/tutorial04.txt
--- a/docs/intro/tutorial04.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/intro/tutorial04.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -16,11 +16,13 @@
 
 .. code-block:: html+django
 
+    {% load csrf %}
     <h1>{{ poll.question }}</h1>
 
     {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
 
     <form action="/polls/{{ poll.id }}/vote/" method="post">
+    {% csrf_token %}
     {% for choice in poll.choice_set.all %}
         <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
         <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
@@ -46,6 +48,30 @@
     * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone
       through its loop
 
+    * Since we are creating a POST form (which can have the effect of modifying
+      data), we unfortunately need to worry about Cross Site Request Forgeries.
+      Thankfully, you don't have to worry too hard, because Django comes with
+      very easy-to-use system for protecting against it.  In short, all POST
+      forms that are targetted at internal URLs need the ``{% csrf_token %}``
+      template tag adding, and this tag in turn requires the ``{% load csrf %}``
+      line.  A 'middleware' which is installed by default checks for the
+      presence of the token in the submitted form data.
+
+The ``{% csrf_token %}`` tag requires information from the request object, which
+is not normally accessible from within the template context.  To fix this, a
+small adjustment needs to be made to the ``detail`` view, so that it looks like
+the following::
+
+    from django.template import RequestContext
+    # ...
+    def detail(request, poll_id):
+        p = get_object_or_404(Poll, pk=poll_id)
+        return render_to_response('polls/detail.html', {'poll': p},
+                                   context_instance=RequestContext(request))
+
+The details of how this works are explained in the documentation for
+:ref:`RequestContext <subclassing-context-requestcontext>`.
+
 Now, let's create a Django view that handles the submitted data and does
 something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we
 created a URLconf for the polls application that includes this line::
@@ -57,6 +83,7 @@
     from django.shortcuts import get_object_or_404, render_to_response
     from django.http import HttpResponseRedirect
     from django.core.urlresolvers import reverse
+    from django.template import RequestContext
     from mysite.polls.models import Choice, Poll
     # ...
     def vote(request, poll_id):
@@ -68,7 +95,7 @@
             return render_to_response('polls/detail.html', {
                 'poll': p,
                 'error_message': "You didn't select a choice.",
-            })
+            }, context_instance=RequestContext(request))
         else:
             selected_choice.votes += 1
             selected_choice.save()
diff -r 1b4df7524b7c docs/ref/contrib/csrf.txt
--- a/docs/ref/contrib/csrf.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/ref/contrib/csrf.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -7,46 +7,180 @@
 .. 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.  A related type of attack, 'login CSRF',
+where an attacking site tricks a user's browser into logging into a site with
+someone else's credentials, is also covered.
 
-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 following the steps
+below.
+
+.. versionadded:: 1.2
+    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.2
+    The template tag functionality (the recommended way to use this) was added
+    in version 1.2. 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`.  (It should come
+       before ``CsrfResponseMiddleware`` if that is being used, and 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 %}
+
+       This should not be done for POST forms that target external URLs, since
+       that would cause the CSRF token to be leaked, leading to a vulnerability.
+
+    4. In the corresponding view functions, ensure that the
+       ``'django.contrib.csrf.context_processors.csrf'`` context processor 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.1, 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.1 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.  It also must come after
+       ``CsrfViewMiddleware``.
+
+Use of the ``CsrfResponseMiddleware`` is not recommended because of the
+performance hit it imposes, and because of a potential security problem (see
+below).  It can be used as an interim measure until applications have been
+updated to use the ``{% crsf_token %}`` tag.  It is deprecated and will be
+removed in Django 1.4.
+
+Django 1.1 and earlier provided a single ``CsrfMiddleware`` class.  This is also
+still available for backwards compatibility.  It combines the functions of the
+two 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.
+
+Security of legacy method
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The post-processing ``CsrfResponseMiddleware`` adds the CSRF token to all POST
+forms (unless the view has been decorated with ``csrf_response_exempt``).  If
+the POST form has an external untrusted site as its target, rather than an
+internal page, that site will be sent the CSRF token when the form is submitted.
+Armed with this leaked information, that site will then be able to successfully
+launch a CSRF attack on your site against that user.  The
+``@csrf_response_exempt`` decorator can be used to fix this, but only if the
+page doesn't also contain internal forms that require the token.
+
+Upgrading notes
+---------------
+
+When upgrading to version 1.2 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`` (in that order).
+
+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 strongly 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.2 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 a potential vulnerability, and any
+views that have been upgraded to use the new template tag method no longer need
+it.
 
 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,71 +188,112 @@
         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.)
+
+Subdomains
+----------
+
+By default, CSRF cookies are specific to the subdomain they are set for.  This
+means that a form served from one subdomain (e.g. server1.example.com) will not
+be able to have a target on another subdomain (e.g. server2.example.com).  This
+restriction can be removed by setting :setting:`CSRF_COOKIE_DOMAIN` to be
+something like ``".example.com"``.
+
+Please note that, with or without use of this setting, this CSRF protection
+mechanism is not safe against cross-subdomain attacks -- see `Limitations`_.
+
+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 the following 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 (a session independent nonce, as
+   it is called), 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``.  It is meant to be permanent,
+   but since there is no way to set a cookie that never expires, it is sent with
+   every response that has called ``django.contrib.csrf.middleware.get_token()``
+   (the function used internally to retrieve the CSRF token).
 
-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 the value of the CSRF
+   cookie.
+
+   This part is done by the template tag (and with the legacy method, it is done
+   by ``CsrfResponseMiddleware``).
+
+3. For all incoming POST requests, a CSRF cookie must be present, and the
+   'csrfmiddlewaretoken' field must be present and correct. If it isn't, the
+   user will get a 403 error.
+
+   This check is done by ``CsrfViewMiddleware``.
+
+4. In addition, for HTTPS requests, strict referer checking is done by
+   ``CsrfViewMiddleware``.  This is necessary to address a Man-In-The-Middle
+   attack that is possible under HTTPS when using a session independent nonce,
+   due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted
+   by clients that are talking to a site under HTTPS.  (Referer checking is not
+   done for HTTP requests, because under HTTP, the presence of the Referer
+   header is not reliable enough.)
+
+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.
-
-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.
-
-The Content-Type is checked before modifying the response, and only
-pages that are served as 'text/html' or 'application/xml+xhtml'
-are modified.
+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.
 
 The middleware tries to be smart about requests that come in via AJAX. Many
 JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP header;
 these requests are detected and automatically *not* handled by this middleware.
 We can do this safely because, in the context of a browser, the header can only
 be added by using ``XMLHttpRequest``, and browsers already implement a
-same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you
-don't trust content within the same domain or subdomains.)
+same-domain policy for ``XMLHttpRequest``.
 
+``CsrfResponseMiddleware`` checks the Content-Type before modifying the
+response, and only pages that are served as 'text/html' or
+'application/xml+xhtml' are modified.
 
 .. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
 
 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.
+Subdomains within a site will be able to set cookies on the client for the whole
+domain.  By setting the cookie and using a corresponding token, subdomains will
+be able to circumvent the CSRF protection.  The only way to avoid this is to
+ensure that subdomains are controlled by trusted users (or, are at least unable
+to set cookies).  Note that even without CSRF, there are other vulnerabilities,
+such as session fixation, that make giving subdomains to untrusted parties a bad
+idea, and these vulnerabilities cannot easily be fixed with current browsers.
 
-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.  You should
+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.
diff -r 1b4df7524b7c docs/ref/contrib/formtools/form-wizard.txt
--- a/docs/ref/contrib/formtools/form-wizard.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/ref/contrib/formtools/form-wizard.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -174,10 +174,11 @@
 .. code-block:: html+django
 
     {% extends "base.html" %}
+    {% load csrf %}
 
     {% block content %}
     <p>Step {{ step }} of {{ step_count }}</p>
-    <form action="." method="post">
+    <form action="." method="post">{% csrf_token %}
     <table>
     {{ form }}
     </table>
diff -r 1b4df7524b7c docs/ref/settings.txt
--- a/docs/ref/settings.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/ref/settings.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -144,6 +144,38 @@
 The default number of seconds to cache a page when the caching middleware or
 ``cache_page()`` decorator is used.
 
+.. setting:: CSRF_COOKIE_NAME
+
+CSRF_COOKIE_NAME
+----------------
+Default: ``'csrftoken'``
+
+The name of the cookie to use for the CSRF authentication token. This can be whatever you
+want.  See :ref:`ref-contrib-csrf`.
+
+.. setting:: CSRF_COOKIE_DOMAIN
+
+CSRF_COOKIE_DOMAIN
+------------------
+
+Default: ``None``
+
+The domain to be used when setting the CSRF cookie.  This can be useful for
+allowing cross-subdomain requests to be exluded from the normal cross site
+request forgery protection.  It should be set to a string such as
+``".lawrence.com"`` to allow a POST request from a form on one subdomain to be
+accepted by accepted by a view served from another subdomain.
+
+.. setting:: CSRF_FAILURE_VIEW
+
+CSRF_FAILURE_VIEW
+-----------------
+
+Default: ``'django.contrib.csrf.views.csrf_failure'``
+
+A dotted path to the view function to be used when an incoming request
+is rejected by the CSRF protection. See :ref:`ref-contrib-csrf`.
+
 .. setting:: DATABASE_ENGINE
 
 DATABASE_ENGINE
@@ -751,6 +783,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`.
diff -r 1b4df7524b7c docs/ref/templates/api.txt
--- a/docs/ref/templates/api.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/ref/templates/api.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -311,7 +311,8 @@
     ("django.core.context_processors.auth",
     "django.core.context_processors.debug",
     "django.core.context_processors.i18n",
-    "django.core.context_processors.media")
+    "django.core.context_processors.media",
+    "django.contrib.csrf.context_processors.csrf")
 
 Each processor is applied in order. That means, if one processor adds a
 variable to the context and a second processor adds a variable with the same
@@ -404,6 +405,14 @@
 ``RequestContext`` will contain a variable ``MEDIA_URL``, providing the
 value of the :setting:`MEDIA_URL` setting.
 
+django.contrib.csrf.context_processors.csrf
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.2
+
+This processor adds a token that is needed by the ``csrf_token`` template tag
+for protection against :ref:`Cross Site Request Forgeries <ref-contrib-csrf>`.
+
 django.core.context_processors.request
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff -r 1b4df7524b7c docs/topics/auth.txt
--- a/docs/topics/auth.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/topics/auth.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -754,6 +754,7 @@
     .. code-block:: html
 
         {% extends "base.html" %}
+        {% load csrf %}
 
         {% block content %}
 
@@ -761,7 +762,7 @@
         <p>Your username and password didn't match. Please try again.</p>
         {% endif %}
 
-        <form method="post" action="{% url django.contrib.auth.views.login %}">
+        <form method="post" action="{% url django.contrib.auth.views.login %}">{% csrf_token %}
         <table>
         <tr>
             <td>{{ form.username.label_tag }}</td>
diff -r 1b4df7524b7c docs/topics/http/middleware.txt
--- a/docs/topics/http/middleware.txt	Mon Aug 24 15:45:48 2009 +0000
+++ b/docs/topics/http/middleware.txt	Mon Aug 31 17:40:40 2009 +0100
@@ -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',
     )
 
diff -r 1b4df7524b7c tests/regressiontests/admin_views/tests.py
--- a/tests/regressiontests/admin_views/tests.py	Mon Aug 24 15:45:48 2009 +0000
+++ b/tests/regressiontests/admin_views/tests.py	Mon Aug 31 17:40:40 2009 +0100
@@ -874,7 +874,7 @@
         # 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
+        # 6 + 2 + 4 + 1 + 2 + 1 = 15 inputs
         self.failUnlessEqual(response.content.count("<input"), 15)
         # 1 select per object = 3 selects
         self.failUnlessEqual(response.content.count("<select"), 4)
diff -r 1b4df7524b7c tests/runtests.py
--- a/tests/runtests.py	Mon Aug 24 15:45:48 2009 +0000
+++ b/tests/runtests.py	Mon Aug 31 17:40:40 2009 +0100
@@ -30,6 +30,7 @@
     'django.contrib.sessions',
     'django.contrib.comments',
     'django.contrib.admin',
+    'django.contrib.csrf',
 ]
 
 def get_test_models():
