Ticket #9977: csrf_template_tag_r11587_1.diff
File csrf_template_tag_r11587_1.diff, 90.4 KB (added by , 15 years ago) |
---|
-
AUTHORS
diff -r 27807883e23f AUTHORS
a b 470 470 Gasper Zejn <zejn@kiberpipa.org> 471 471 Jarek Zgoda <jarek.zgoda@gmail.com> 472 472 Cheng Zhang 473 Glenn 474 bthomas 473 475 474 476 A big THANK YOU goes to: 475 477 -
django/conf/global_settings.py
diff -r 27807883e23f django/conf/global_settings.py
a b 300 300 MIDDLEWARE_CLASSES = ( 301 301 'django.middleware.common.CommonMiddleware', 302 302 'django.contrib.sessions.middleware.SessionMiddleware', 303 'django.contrib.csrf.middleware.CsrfViewMiddleware', 303 304 'django.contrib.auth.middleware.AuthenticationMiddleware', 304 305 # 'django.middleware.http.ConditionalGetMiddleware', 305 306 # 'django.middleware.gzip.GZipMiddleware', … … 374 375 # The number of days a password reset link is valid for 375 376 PASSWORD_RESET_TIMEOUT_DAYS = 3 376 377 378 ######## 379 # CSRF # 380 ######## 381 382 # Dotted path to callable to be used as view when a request is 383 # rejected by the CSRF middleware. 384 CSRF_FAILURE_VIEW = 'django.contrib.csrf.views.csrf_failure' 385 386 # Name and domain for CSRF cookie. 387 CSRF_COOKIE_NAME = 'csrftoken' 388 CSRF_COOKIE_DOMAIN = None 389 377 390 ########### 378 391 # TESTING # 379 392 ########### -
django/conf/project_template/settings.py
diff -r 27807883e23f django/conf/project_template/settings.py
a b 60 60 MIDDLEWARE_CLASSES = ( 61 61 'django.middleware.common.CommonMiddleware', 62 62 'django.contrib.sessions.middleware.SessionMiddleware', 63 'django.contrib.csrf.middleware.CsrfViewMiddleware', 63 64 'django.contrib.auth.middleware.AuthenticationMiddleware', 64 65 ) 65 66 -
django/contrib/admin/options.py
diff -r 27807883e23f django/contrib/admin/options.py
a b 6 6 from django.contrib.admin import widgets 7 7 from django.contrib.admin import helpers 8 8 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict 9 from django.contrib.csrf.decorators import csrf_protect 9 10 from django.core.exceptions import PermissionDenied 10 11 from django.db import models, transaction 11 12 from django.db.models.fields import BLANK_CHOICE_DASH … … 782 783 } 783 784 context.update(extra_context or {}) 784 785 return self.render_change_form(request, context, form_url=form_url, add=True) 785 add_view = transaction.commit_on_success(add_view)786 add_view = csrf_protect(transaction.commit_on_success(add_view)) 786 787 787 788 def change_view(self, request, object_id, extra_context=None): 788 789 "The 'change' admin view for this model." … … 871 872 } 872 873 context.update(extra_context or {}) 873 874 return self.render_change_form(request, context, change=True, obj=obj) 874 change_view = transaction.commit_on_success(change_view)875 change_view = csrf_protect(transaction.commit_on_success(change_view)) 875 876 876 877 def changelist_view(self, request, extra_context=None): 877 878 "The 'change list' admin view for this model." … … 984 985 'admin/%s/change_list.html' % app_label, 985 986 'admin/change_list.html' 986 987 ], context, context_instance=context_instance) 988 changelist_view = csrf_protect(changelist_view) 987 989 988 990 def delete_view(self, request, object_id, extra_context=None): 989 991 "The 'delete' admin view for this model." … … 1040 1042 "admin/%s/delete_confirmation.html" % app_label, 1041 1043 "admin/delete_confirmation.html" 1042 1044 ], context, context_instance=context_instance) 1045 delete_view = csrf_protect(delete_view) 1043 1046 1044 1047 def history_view(self, request, object_id, extra_context=None): 1045 1048 "The 'history' admin view for this model." -
django/contrib/admin/sites.py
diff -r 27807883e23f django/contrib/admin/sites.py
a b 3 3 from django.contrib.admin import ModelAdmin 4 4 from django.contrib.admin import actions 5 5 from django.contrib.auth import authenticate, login 6 from django.contrib.csrf.middleware import csrf_response_exempt 7 from django.contrib.csrf.decorators import csrf_protect 6 8 from django.db.models.base import ModelBase 7 9 from django.core.exceptions import ImproperlyConfigured 8 10 from django.core.urlresolvers import reverse … … 186 188 return view(request, *args, **kwargs) 187 189 if not cacheable: 188 190 inner = never_cache(inner) 191 # We add csrf_protect here so this function can be used as a utility 192 # function for any view, without having to repeat 'csrf_protect'. 193 inner = csrf_response_exempt(csrf_protect(inner)) 189 194 return update_wrapper(inner, view) 190 195 191 196 def get_urls(self): -
django/contrib/admin/templates/admin/auth/user/change_password.html
diff -r 27807883e23f django/contrib/admin/templates/admin/auth/user/change_password.html
a b 15 15 </div> 16 16 {% endif %}{% endblock %} 17 17 {% block content %}<div id="content-main"> 18 <form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}18 <form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %} 19 19 <div> 20 20 {% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %} 21 21 {% if form.errors %} -
django/contrib/admin/templates/admin/change_form.html
diff -r 27807883e23f django/contrib/admin/templates/admin/change_form.html
a b 29 29 </ul> 30 30 {% endif %}{% endif %} 31 31 {% endblock %} 32 <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}32 <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %} 33 33 <div> 34 34 {% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %} 35 35 {% if save_on_top %}{% submit_row %}{% endif %} -
django/contrib/admin/templates/admin/change_list.html
diff -r 27807883e23f django/contrib/admin/templates/admin/change_list.html
a b 68 68 {% endif %} 69 69 {% endblock %} 70 70 71 <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}> 71 <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>{% csrf_token %} 72 72 {% if cl.formset %} 73 73 {{ cl.formset.management_form }} 74 74 {% endif %} -
django/contrib/admin/templates/admin/delete_confirmation.html
diff -r 27807883e23f django/contrib/admin/templates/admin/delete_confirmation.html
a b 22 22 {% else %} 23 23 <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> 24 24 <ul>{{ deleted_objects|unordered_list }}</ul> 25 <form action="" method="post"> 25 <form action="" method="post">{% csrf_token %} 26 26 <div> 27 27 <input type="hidden" name="post" value="yes" /> 28 28 <input type="submit" value="{% trans "Yes, I'm sure" %}" /> -
django/contrib/admin/templates/admin/delete_selected_confirmation.html
diff -r 27807883e23f django/contrib/admin/templates/admin/delete_selected_confirmation.html
a b 23 23 {% for deleteable_object in deletable_objects %} 24 24 <ul>{{ deleteable_object|unordered_list }}</ul> 25 25 {% endfor %} 26 <form action="" method="post"> 26 <form action="" method="post">{% csrf_token %} 27 27 <div> 28 28 {% for obj in queryset %} 29 29 <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk }}" /> -
django/contrib/admin/templates/admin/login.html
diff -r 27807883e23f django/contrib/admin/templates/admin/login.html
a b 14 14 <p class="errornote">{{ error_message }}</p> 15 15 {% endif %} 16 16 <div id="content-main"> 17 <form action="{{ app_path }}" method="post" id="login-form"> 17 <form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %} 18 18 <div class="form-row"> 19 19 <label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" /> 20 20 </div> -
django/contrib/admin/templates/admin/template_validator.html
diff -r 27807883e23f django/contrib/admin/templates/admin/template_validator.html
a b 4 4 5 5 <div id="content-main"> 6 6 7 <form action="" method="post"> 7 <form action="" method="post">{% csrf_token %} 8 8 9 9 {% if form.errors %} 10 10 <p class="errornote">Your template had {{ form.errors|length }} error{{ form.errors|pluralize }}:</p> -
django/contrib/admin/templates/registration/password_change_form.html
diff -r 27807883e23f django/contrib/admin/templates/registration/password_change_form.html
a b 11 11 12 12 <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> 13 13 14 <form action="" method="post"> 14 <form action="" method="post">{% csrf_token %} 15 15 16 16 {{ form.old_password.errors }} 17 17 <p class="aligned wide"><label for="id_old_password">{% trans 'Old password:' %}</label>{{ form.old_password }}</p> -
django/contrib/admin/templates/registration/password_reset_confirm.html
diff -r 27807883e23f django/contrib/admin/templates/registration/password_reset_confirm.html
a b 13 13 14 14 <p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p> 15 15 16 <form action="" method="post"> 16 <form action="" method="post">{% csrf_token %} 17 17 {{ form.new_password1.errors }} 18 18 <p class="aligned wide"><label for="id_new_password1">{% trans 'New password:' %}</label>{{ form.new_password1 }}</p> 19 19 {{ form.new_password2.errors }} -
django/contrib/admin/templates/registration/password_reset_form.html
diff -r 27807883e23f django/contrib/admin/templates/registration/password_reset_form.html
a b 11 11 12 12 <p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." %}</p> 13 13 14 <form action="" method="post"> 14 <form action="" method="post">{% csrf_token %} 15 15 {{ form.email.errors }} 16 16 <p><label for="id_email">{% trans 'E-mail address:' %}</label> {{ form.email }} <input type="submit" value="{% trans 'Reset my password' %}" /></p> 17 17 </form> -
django/contrib/auth/views.py
diff -r 27807883e23f django/contrib/auth/views.py
a b 4 4 from django.contrib.auth.forms import AuthenticationForm 5 5 from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm, PasswordChangeForm 6 6 from django.contrib.auth.tokens import default_token_generator 7 from django.contrib.csrf.decorators import csrf_protect 7 8 from django.core.urlresolvers import reverse 8 9 from django.shortcuts import render_to_response, get_object_or_404 9 10 from django.contrib.sites.models import Site, RequestSite … … 41 42 'site': current_site, 42 43 'site_name': current_site.name, 43 44 }, context_instance=RequestContext(request)) 44 login = never_cache(login)45 login = csrf_protect(never_cache(login)) 45 46 46 47 def logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name=REDIRECT_FIELD_NAME): 47 48 "Logs out the user and displays 'You are logged out' message." … … 103 104 return render_to_response(template_name, { 104 105 'form': form, 105 106 }, context_instance=RequestContext(request)) 107 password_reset = csrf_protect(password_reset) 106 108 107 109 def password_reset_done(request, template_name='registration/password_reset_done.html'): 108 110 return render_to_response(template_name, context_instance=RequestContext(request)) … … 139 141 form = None 140 142 context_instance['form'] = form 141 143 return render_to_response(template_name, context_instance=context_instance) 144 # Doesn't need csrf_protect since no-one can guess the URL 142 145 143 146 def password_reset_complete(request, template_name='registration/password_reset_complete.html'): 144 147 return render_to_response(template_name, context_instance=RequestContext(request, … … 158 161 return render_to_response(template_name, { 159 162 'form': form, 160 163 }, context_instance=RequestContext(request)) 161 password_change = login_required(password_change)164 password_change = csrf_protect(login_required(password_change)) 162 165 163 166 def password_change_done(request, template_name='registration/password_change_done.html'): 164 167 return render_to_response(template_name, context_instance=RequestContext(request)) -
django/contrib/comments/templates/comments/approve.html
diff -r 27807883e23f django/contrib/comments/templates/comments/approve.html
a b 6 6 {% block content %} 7 7 <h1>{% trans "Really make this comment public?" %}</h1> 8 8 <blockquote>{{ comment|linebreaks }}</blockquote> 9 <form action="." method="post"> 9 <form action="." method="post">{% csrf_token %} 10 10 {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %} 11 11 <p class="submit"> 12 12 <input type="submit" name="submit" value="{% trans "Approve" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a> -
django/contrib/comments/templates/comments/delete.html
diff -r 27807883e23f django/contrib/comments/templates/comments/delete.html
a b 6 6 {% block content %} 7 7 <h1>{% trans "Really remove this comment?" %}</h1> 8 8 <blockquote>{{ comment|linebreaks }}</blockquote> 9 <form action="." method="post"> 9 <form action="." method="post">{% csrf_token %} 10 10 {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %} 11 11 <p class="submit"> 12 12 <input type="submit" name="submit" value="{% trans "Remove" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a> -
django/contrib/comments/templates/comments/flag.html
diff -r 27807883e23f django/contrib/comments/templates/comments/flag.html
a b 6 6 {% block content %} 7 7 <h1>{% trans "Really flag this comment?" %}</h1> 8 8 <blockquote>{{ comment|linebreaks }}</blockquote> 9 <form action="." method="post"> 9 <form action="." method="post">{% csrf_token %} 10 10 {% if next %}<input type="hidden" name="next" value="{{ next }}" id="next" />{% endif %} 11 11 <p class="submit"> 12 12 <input type="submit" name="submit" value="{% trans "Flag" %}" /> or <a href="{{ comment.get_absolute_url }}">cancel</a> -
django/contrib/comments/templates/comments/form.html
diff -r 27807883e23f django/contrib/comments/templates/comments/form.html
a b 1 1 {% load comments i18n %} 2 <form action="{% comment_form_target %}" method="post"> 2 <form action="{% comment_form_target %}" method="post">{% csrf_token %} 3 3 {% if next %}<input type="hidden" name="next" value="{{ next }}" />{% endif %} 4 4 {% for field in form %} 5 5 {% if field.is_hidden %} -
django/contrib/comments/templates/comments/moderation_queue.html
diff -r 27807883e23f django/contrib/comments/templates/comments/moderation_queue.html
a b 44 44 {% for comment in comments %} 45 45 <tr class="{% cycle 'row1' 'row2' %}"> 46 46 <td class="actions"> 47 <form action="{% url comments-approve comment.pk %}" method="post"> 47 <form action="{% url comments-approve comment.pk %}" method="post">{% csrf_token %} 48 48 <input type="hidden" name="next" value="{% url comments-moderation-queue %}" /> 49 49 <input class="approve submit" type="submit" name="submit" value="{% trans "Approve" %}" /> 50 50 </form> 51 <form action="{% url comments-delete comment.pk %}" method="post"> 51 <form action="{% url comments-delete comment.pk %}" method="post">{% csrf_token %} 52 52 <input type="hidden" name="next" value="{% url comments-moderation-queue %}" /> 53 53 <input class="remove submit" type="submit" name="submit" value="{% trans "Remove" %}" /> 54 54 </form> -
django/contrib/comments/templates/comments/preview.html
diff -r 27807883e23f django/contrib/comments/templates/comments/preview.html
a b 5 5 6 6 {% block content %} 7 7 {% load comments %} 8 <form action="{% comment_form_target %}" method="post"> 8 <form action="{% comment_form_target %}" method="post">{% csrf_token %} 9 9 {% if next %}<input type="hidden" name="next" value="{{ next }}" />{% endif %} 10 10 {% if form.errors %} 11 11 <h1>{% blocktrans count form.errors|length as counter %}Please correct the error below{% plural %}Please correct the errors below{% endblocktrans %}</h1> -
django/contrib/comments/views/comments.py
diff -r 27807883e23f django/contrib/comments/views/comments.py
a b 10 10 from django.views.decorators.http import require_POST 11 11 from django.contrib import comments 12 12 from django.contrib.comments import signals 13 from django.contrib.csrf.decorators import csrf_protect 13 14 14 15 class CommentPostBadRequest(http.HttpResponseBadRequest): 15 16 """ … … 116 117 117 118 return next_redirect(data, next, comment_done, c=comment._get_pk_val()) 118 119 119 post_comment = require_POST(post_comment)120 post_comment = csrf_protect(require_POST(post_comment)) 120 121 121 122 comment_done = confirmation_view( 122 123 template = "comments/posted.html", -
django/contrib/comments/views/moderation.py
diff -r 27807883e23f django/contrib/comments/views/moderation.py
a b 7 7 from django.http import Http404 8 8 from django.contrib import comments 9 9 from django.contrib.comments import signals 10 from django.contrib.csrf.decorators import csrf_protect 10 11 11 12 #@login_required 12 13 def flag(request, comment_id, next=None): … … 42 43 {'comment': comment, "next": next}, 43 44 template.RequestContext(request) 44 45 ) 45 flag = login_required(flag)46 flag = csrf_protect(login_required(flag)) 46 47 47 48 #@permission_required("comments.delete_comment") 48 49 def delete(request, comment_id, next=None): … … 82 83 {'comment': comment, "next": next}, 83 84 template.RequestContext(request) 84 85 ) 85 delete = permission_required("comments.can_moderate")(delete)86 delete = csrf_protect(permission_required("comments.can_moderate")(delete)) 86 87 87 88 #@permission_required("comments.can_moderate") 88 89 def approve(request, comment_id, next=None): … … 126 127 template.RequestContext(request) 127 128 ) 128 129 129 approve = permission_required("comments.can_moderate")(approve)130 approve = csrf_protect(permission_required("comments.can_moderate")(approve)) 130 131 131 132 132 133 #@permission_required("comments.can_moderate") -
new file django/contrib/csrf/context_processors.py
diff -r 27807883e23f django/contrib/csrf/context_processors.py
- + 1 from django.contrib.csrf.middleware import get_token 2 from django.utils.functional import lazy 3 4 def csrf(request): 5 """ 6 Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if 7 it has not been provided by either a view decorator or the middleware 8 """ 9 def _get_val(): 10 token = get_token(request) 11 if token is None: 12 # In order to be able to provide debugging info in the 13 # case of misconfiguration, we use a sentinel value 14 # instead of returning an empty dict. 15 return 'NOTPROVIDED' 16 else: 17 return token 18 _get_val = lazy(_get_val, str) 19 20 return {'csrf_token': _get_val() } -
new file django/contrib/csrf/decorators.py
diff -r 27807883e23f django/contrib/csrf/decorators.py
- + 1 from django.contrib.csrf.middleware import CsrfViewMiddleware 2 from django.utils.decorators import decorator_from_middleware 3 4 csrf_protect = decorator_from_middleware(CsrfViewMiddleware) 5 csrf_protect.__name__ = "csrf_protect" 6 csrf_protect.__doc__ = """ 7 This decorator adds CSRF protection in exactly the same way as 8 CsrfViewMiddleware, but it can be used on a per view basis. Using both, or 9 using the decorator multiple times, is harmless and efficient. 10 """ -
django/contrib/csrf/middleware.py
diff -r 27807883e23f django/contrib/csrf/middleware.py
a b 5 5 against request forgeries from other sites. 6 6 """ 7 7 8 import itertools 8 9 import re 9 import itertools10 import random 10 11 try: 11 12 from functools import wraps 12 13 except ImportError: 13 14 from django.utils.functional import wraps # Python 2.3, 2.4 fallback. 14 15 15 16 from django.conf import settings 16 from django.http import HttpResponseForbidden 17 from django.core.urlresolvers import get_callable 18 from django.utils.cache import patch_vary_headers 17 19 from django.utils.hashcompat import md5_constructor 18 20 from django.utils.safestring import mark_safe 19 21 20 _ERROR_MSG = mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>')21 22 22 _POST_FORM_RE = \ 23 23 re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE) 24 24 25 25 _HTML_TYPES = ('text/html', 'application/xhtml+xml') 26 26 27 def _make_token(session_id): 27 # Use the system (hardware-based) random number generator if it exists. 28 if hasattr(random, 'SystemRandom'): 29 randrange = random.SystemRandom().randrange 30 else: 31 randrange = random.randrange 32 _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63 33 34 def _get_failure_view(): 35 """ 36 Returns the view to be used for CSRF rejections 37 """ 38 return get_callable(settings.CSRF_FAILURE_VIEW) 39 40 def _get_new_csrf_key(): 41 return md5_constructor("%s%s" 42 % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest() 43 44 def _make_legacy_session_token(session_id): 28 45 return md5_constructor(settings.SECRET_KEY + session_id).hexdigest() 29 46 47 def get_token(request): 48 """ 49 Returns the the CSRF token required for a POST form. 50 """ 51 request.META["CSRF_COOKIE_USED"] = True 52 return request.META.get("CSRF_COOKIE", None) 53 30 54 class CsrfViewMiddleware(object): 31 55 """ 32 56 Middleware that requires a present and correct csrfmiddlewaretoken 33 for POST requests that have an active session. 57 for POST requests that have a CSRF cookie, and sets an outgoing 58 CSRF cookie. 59 60 This middleware should be used in conjunction with the csrf_token template 61 tag. 34 62 """ 35 63 def process_view(self, request, callback, callback_args, callback_kwargs): 64 if getattr(callback, 'csrf_exempt', False): 65 return None 66 67 if getattr(request, 'csrf_processing_done', False): 68 return None 69 70 reject = lambda s: _get_failure_view()(request, reason=s) 71 def accept(): 72 # Avoid checking the request twice by adding a custom attribute to 73 # request. This will be relevant when both decorator and middleware 74 # are used. 75 request.csrf_processing_done = True 76 return None 77 78 # If the user doesn't have a CSRF cookie, generate one and store it in the 79 # request, so it's available to the view. We'll store it in a cookie when 80 # we reach the response. 81 try: 82 request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME] 83 cookie_is_new = False 84 except KeyError: 85 # No cookie, so create one. 86 request.META["CSRF_COOKIE"] = _get_new_csrf_key() 87 cookie_is_new = True 88 36 89 if request.method == 'POST': 37 if getattr(callback, 'csrf_exempt', False): 38 return None 90 if getattr(request, 'dont_enforce_csrf_checks', False): 91 # Mechanism to turn off CSRF checks for test suite. It comes after 92 # the creation of CSRF cookies, so that everything else continues to 93 # work exactly the same (e.g. cookies are sent etc), but before the 94 # any branches that call reject() 95 return accept() 39 96 40 97 if request.is_ajax(): 41 return None 98 # .is_ajax() is based on the presence of X-Requested-With. In 99 # the context of a browser, this can only be sent if using 100 # XmlHttpRequest. Browsers implement careful policies for 101 # XmlHttpRequest: 102 # 103 # * Normally, only same-domain requests are allowed. 104 # 105 # * Some browsers (e.g. Firefox 3.5 and later) relax this 106 # carefully: 107 # 108 # * if it is a 'simple' GET or POST request (which can 109 # include no custom headers), it is allowed to be cross 110 # domain. These requests will not be recognized as AJAX. 111 # 112 # * if a 'preflight' check with the server confirms that the 113 # server is expecting and allows the request, cross domain 114 # requests even with custom headers are allowed. These 115 # requests will be recognized as AJAX, but can only get 116 # through when the developer has specifically opted in to 117 # allowing the cross-domain POST request. 118 # 119 # So in all cases, it is safe to allow these requests through. 120 return accept() 42 121 43 try:44 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]45 except KeyError:46 # No session, no check required47 return None122 if request.is_secure(): 123 # Strict referer checking for HTTPS 124 referer = request.META.get('HTTP_REFERER') 125 if referer is None: 126 return reject("Referer checking failed - no Referer.") 48 127 49 csrf_token = _make_token(session_id) 128 # The following check ensures that the referer is HTTPS, 129 # the domains match and the ports match. This might be too strict. 130 good_referer = 'https://%s/' % request.get_host() 131 if not referer.startswith(good_referer): 132 return reject("Referer checking failed - %s does not match %s." % 133 (referer, good_referer)) 134 135 # If the user didn't already have a CSRF key, then accept the 136 # session key for the middleware token, so CSRF protection isn't lost 137 # for the period between upgrading to CSRF cookes to the first time 138 # each user comes back to the site to receive one. 139 if cookie_is_new: 140 try: 141 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] 142 csrf_token = _make_legacy_session_token(session_id) 143 except KeyError: 144 # No CSRF cookie and no session cookie. For POST requests, 145 # we insist on a CSRF cookie, and in this way we can avoid 146 # all CSRF attacks, including login CSRF. 147 return reject("No CSRF cookie.") 148 else: 149 csrf_token = request.META["CSRF_COOKIE"] 150 50 151 # check incoming token 51 try: 52 request_csrf_token = request.POST['csrfmiddlewaretoken'] 53 except KeyError: 54 return HttpResponseForbidden(_ERROR_MSG) 152 request_csrf_token = request.POST.get('csrfmiddlewaretoken', None) 153 if request_csrf_token != csrf_token: 154 return reject("CSRF token missing or incorrect.") 55 155 56 if request_csrf_token != csrf_token: 57 return HttpResponseForbidden(_ERROR_MSG) 156 return accept() 58 157 59 return None 158 def process_response(self, request, response): 159 if getattr(response, 'csrf_processing_done', False): 160 return response 161 162 # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was 163 # never called, probaby because a request middleware returned a response 164 # (for example, contrib.auth redirecting to a login page). 165 if request.META.get("CSRF_COOKIE") is None: 166 return response 167 168 if not request.META.get("CSRF_COOKIE_USED", False): 169 return response 170 171 # Set the CSRF cookie even if it's already set, so we renew the expiry timer. 172 response.set_cookie(settings.CSRF_COOKIE_NAME, 173 request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52, 174 domain=settings.CSRF_COOKIE_DOMAIN) 175 # Content varies with the CSRF cookie, so set the Vary header. 176 patch_vary_headers(response, ('Cookie',)) 177 response.csrf_processing_done = True 178 return response 60 179 61 180 class CsrfResponseMiddleware(object): 62 181 """ 63 Middleware that post-processes a response to add a 64 csrfmiddlewaretoken if the response/request have an active 65 session. 182 DEPRECATED 183 Middleware that post-processes a response to add a csrfmiddlewaretoken. 184 185 This exists for backwards compatibility and as an interim measure until 186 applications are converted to using use the csrf_token template tag 187 instead. It will be removed in Django 1.4. 66 188 """ 189 def __init__(self): 190 import warnings 191 warnings.warn( 192 "CsrfResponseMiddleware and CsrfMiddleware are deprecated; use CsrfViewMiddleware and the template tag instead (see CSRF documentation).", 193 PendingDeprecationWarning 194 ) 195 67 196 def process_response(self, request, response): 68 197 if getattr(response, 'csrf_exempt', False): 69 198 return response 70 199 71 csrf_token = None 72 try: 73 # This covers a corner case in which the outgoing response 74 # both contains a form and sets a session cookie. This 75 # really should not be needed, since it is best if views 76 # that create a new session (login pages) also do a 77 # redirect, as is done by all such view functions in 78 # Django. 79 cookie = response.cookies[settings.SESSION_COOKIE_NAME] 80 csrf_token = _make_token(cookie.value) 81 except KeyError: 82 # Normal case - look for existing session cookie 83 try: 84 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] 85 csrf_token = _make_token(session_id) 86 except KeyError: 87 # no incoming or outgoing cookie 88 pass 89 90 if csrf_token is not None and \ 91 response['Content-Type'].split(';')[0] in _HTML_TYPES: 200 if response['Content-Type'].split(';')[0] in _HTML_TYPES: 201 csrf_token = get_token(request) 202 # If csrf_token is None, we have no token for this request, which probably 203 # means that this is a response from a request middleware. 204 if csrf_token is None: 205 return response 92 206 93 207 # ensure we don't add the 'id' attribute twice (HTML validity) 94 208 idattributes = itertools.chain(("id='csrfmiddlewaretoken'",), 95 209 itertools.repeat('')) 96 210 def add_csrf_field(match): 97 211 """Returns the matched <form> tag plus the added <input> element""" 98 212 return mark_safe(match.group() + "<div style='display:none;'>" + \ … … 101 215 "' /></div>") 102 216 103 217 # Modify any POST forms 104 response.content = _POST_FORM_RE.sub(add_csrf_field, response.content) 218 response.content, n = _POST_FORM_RE.subn(add_csrf_field, response.content) 219 if n > 0: 220 # Content varies with the CSRF cookie, so set the Vary header. 221 patch_vary_headers(response, ('Cookie',)) 105 222 return response 106 223 107 class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware): 108 """Django middleware that adds protection against Cross Site 224 class CsrfMiddleware(object): 225 """ 226 Django middleware that adds protection against Cross Site 109 227 Request Forgeries by adding hidden form fields to POST forms and 110 228 checking requests for the correct value. 111 229 112 In the list of middlewares, SessionMiddleware is required, and 113 must come after this middleware. CsrfMiddleWare must come after 114 compression middleware. 230 CsrfMiddleware uses two middleware, CsrfViewMiddleware and 231 CsrfResponseMiddleware, which can be used independently. It is recommended 232 to use only CsrfViewMiddleware and use the csrf_token template tag in 233 templates for inserting the token. 234 """ 235 # We can't just inherit from CsrfViewMiddleware and CsrfResponseMiddleware 236 # because both have process_response methods. 237 def __init__(self): 238 self.response_middleware = CsrfResponseMiddleware() 239 self.view_middleware = CsrfViewMiddleware() 115 240 116 If a session ID cookie is present, it is hashed with the 117 SECRET_KEY setting to create an authentication token. This token 118 is added to all outgoing POST forms and is expected on all 119 incoming POST requests that have a session ID cookie. 241 def process_response(self, request, resp): 242 # We must do the response post-processing first, because that calls 243 # get_token(), which triggers a flag saying that the CSRF cookie needs 244 # to be sent (done in CsrfViewMiddleware.process_response) 245 resp2 = self.response_middleware.process_response(request, resp) 246 return self.view_middleware.process_response(request, resp2) 120 247 121 If you are setting cookies directly, instead of using Django's 122 session framework, this middleware will not work. 123 124 CsrfMiddleWare is composed of two middleware, CsrfViewMiddleware 125 and CsrfResponseMiddleware which can be used independently. 126 """ 127 pass 248 def process_view(self, request, callback, callback_args, callback_kwargs): 249 return self.view_middleware.process_view(request, callback, callback_args, 250 callback_kwargs) 128 251 129 252 def csrf_response_exempt(view_func): 130 253 """ -
django/contrib/csrf/tests.py
diff -r 27807883e23f django/contrib/csrf/tests.py
a b 1 1 # -*- coding: utf-8 -*- 2 2 3 3 from django.test import TestCase 4 from django.http import HttpRequest, HttpResponse, HttpResponseForbidden 5 from django.contrib.csrf.middleware import CsrfMiddleware, _make_token, csrf_exempt 4 from django.http import HttpRequest, HttpResponse 5 from django.contrib.csrf.middleware import CsrfMiddleware, CsrfViewMiddleware, csrf_exempt 6 from django.contrib.csrf.context_processors import csrf 7 from django.contrib.sessions.middleware import SessionMiddleware 8 from django.utils.importlib import import_module 6 9 from django.conf import settings 10 from django.template import RequestContext, Template 7 11 8 12 # Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests 9 13 def post_form_response(): 10 14 resp = HttpResponse(content=""" 11 15 <html><body><form method="POST"><input type="text" /></form></body></html> 12 16 """, mimetype="text/html") 13 17 return resp 14 18 15 def test_view(request): 19 def post_form_response_non_html(): 20 resp = post_form_response() 21 resp["Content-Type"] = "application/xml" 22 return resp 23 24 def post_form_view(request): 25 """A view that returns a POST form (without a token)""" 16 26 return post_form_response() 17 27 28 # Response/views used for template tag tests 29 def _token_template(): 30 return Template("{% csrf_token %}") 31 32 def _render_csrf_token_template(req): 33 context = RequestContext(req, processors=[csrf]) 34 template = _token_template() 35 return template.render(context) 36 37 def token_view(request): 38 """A view that uses {% csrf_token %}""" 39 return HttpResponse(_render_csrf_token_template(request)) 40 41 def non_token_view_using_request_processor(request): 42 """ 43 A view that doesn't use the token, but does use the csrf view processor. 44 """ 45 context = RequestContext(request, processors=[csrf]) 46 template = Template("") 47 return HttpResponse(template.render(context)) 48 49 class TestingHttpRequest(HttpRequest): 50 """ 51 A version of HttpRequest that allows us to change some things 52 more easily 53 """ 54 def is_secure(self): 55 return getattr(self, '_is_secure', False) 56 18 57 class CsrfMiddlewareTest(TestCase): 58 _csrf_id = "1" 19 59 60 # This is a valid session token for this ID and secret key. This was generated using 61 # the old code that we're to be backwards-compatible with. Don't use the CSRF code 62 # to generate this hash, or we're merely testing the code against itself and not 63 # checking backwards-compatibility. This is also the output of (echo -n test1 | md5sum). 64 _session_token = "5a105e8b9d40e1329780d62ea2265d8a" 20 65 _session_id = "1" 66 _secret_key_for_session_test= "test" 21 67 22 def _get_GET_no_ session_request(self):23 return HttpRequest()68 def _get_GET_no_csrf_cookie_request(self): 69 return TestingHttpRequest() 24 70 25 def _get_GET_session_request(self): 26 req = self._get_GET_no_session_request() 71 def _get_GET_csrf_cookie_request(self): 72 req = TestingHttpRequest() 73 req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id 74 return req 75 76 def _get_POST_csrf_cookie_request(self): 77 req = self._get_GET_csrf_cookie_request() 78 req.method = "POST" 79 return req 80 81 def _get_POST_no_csrf_cookie_request(self): 82 req = self._get_GET_no_csrf_cookie_request() 83 req.method = "POST" 84 return req 85 86 def _get_POST_request_with_token(self): 87 req = self._get_POST_csrf_cookie_request() 88 req.POST['csrfmiddlewaretoken'] = self._csrf_id 89 return req 90 91 def _get_POST_session_request_with_token(self): 92 req = self._get_POST_no_csrf_cookie_request() 93 req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id 94 req.POST['csrfmiddlewaretoken'] = self._session_token 95 return req 96 97 def _get_POST_session_request_no_token(self): 98 req = self._get_POST_no_csrf_cookie_request() 27 99 req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id 28 100 return req 29 101 30 def _get_POST_session_request(self): 31 req = self._get_GET_session_request() 32 req.method = "POST" 33 return req 102 def _check_token_present(self, response, csrf_id=None): 103 self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id)) 34 104 35 def _get_POST_no_session_request(self): 36 req = self._get_GET_no_session_request() 37 req.method = "POST" 38 return req 105 # Check the post processing and outgoing cookie 106 def test_process_response_no_csrf_cookie(self): 107 """ 108 When no prior CSRF cookie exists, check that the cookie is created and a 109 token is inserted. 110 """ 111 req = self._get_GET_no_csrf_cookie_request() 112 CsrfMiddleware().process_view(req, post_form_view, (), {}) 39 113 40 def _get_POST_session_request_with_token(self): 41 req = self._get_POST_session_request() 42 req.POST['csrfmiddlewaretoken'] = _make_token(self._session_id) 43 return req 44 45 def _get_post_form_response(self): 46 return post_form_response() 47 48 def _get_new_session_response(self): 49 resp = self._get_post_form_response() 50 resp.cookies[settings.SESSION_COOKIE_NAME] = self._session_id 51 return resp 52 53 def _check_token_present(self, response): 54 self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % _make_token(self._session_id)) 55 56 def get_view(self): 57 return test_view 58 59 # Check the post processing 60 def test_process_response_no_session(self): 61 """ 62 Check the post-processor does nothing if no session active 63 """ 64 req = self._get_GET_no_session_request() 65 resp = self._get_post_form_response() 114 resp = post_form_response() 66 115 resp_content = resp.content # needed because process_response modifies resp 67 116 resp2 = CsrfMiddleware().process_response(req, resp) 68 self.assertEquals(resp_content, resp2.content)69 117 70 def test_process_response_existing_session(self): 118 csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) 119 self.assertNotEqual(csrf_cookie, False) 120 self.assertNotEqual(resp_content, resp2.content) 121 self._check_token_present(resp2, csrf_cookie.value) 122 # Check the Vary header got patched correctly 123 self.assert_('Cookie' in resp2.get('Vary','')) 124 125 def test_process_response_no_csrf_cookie_view_only_get_token_used(self): 71 126 """ 72 Check that the token is inserted if there is an existing session 127 When no prior CSRF cookie exists, check that the cookie is created, even 128 if only CsrfViewMiddleware is used. 73 129 """ 74 req = self._get_GET_session_request() 75 resp = self._get_post_form_response() 130 # This is checking that CsrfViewMiddleware has the cookie setting 131 # code. Most of the other tests use CsrfMiddleware. 132 req = self._get_GET_no_csrf_cookie_request() 133 # token_view calls get_token() indirectly 134 CsrfViewMiddleware().process_view(req, token_view, (), {}) 135 resp = token_view(req) 136 resp2 = CsrfViewMiddleware().process_response(req, resp) 137 138 csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) 139 self.assertNotEqual(csrf_cookie, False) 140 141 def test_process_response_get_token_not_used(self): 142 """ 143 Check that if get_token() is not called, the view middleware does not 144 add a cookie. 145 """ 146 # This is important to make pages cacheable. Pages which do call 147 # get_token(), assuming they use the token, are not cacheable because 148 # the token is specific to the user 149 req = self._get_GET_no_csrf_cookie_request() 150 # non_token_view_using_request_processor does not call get_token(), but 151 # does use the csrf request processor. By using this, we are testing 152 # that the view processor is properly lazy and doesn't call get_token() 153 # until needed. 154 CsrfViewMiddleware().process_view(req, non_token_view_using_request_processor, (), {}) 155 resp = non_token_view_using_request_processor(req) 156 resp2 = CsrfViewMiddleware().process_response(req, resp) 157 158 csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) 159 self.assertEqual(csrf_cookie, False) 160 161 def test_process_response_existing_csrf_cookie(self): 162 """ 163 Check that the token is inserted when a prior CSRF cookie exists 164 """ 165 req = self._get_GET_csrf_cookie_request() 166 CsrfMiddleware().process_view(req, post_form_view, (), {}) 167 168 resp = post_form_response() 76 169 resp_content = resp.content # needed because process_response modifies resp 77 170 resp2 = CsrfMiddleware().process_response(req, resp) 78 171 self.assertNotEqual(resp_content, resp2.content) 79 172 self._check_token_present(resp2) 80 173 81 def test_process_response_n ew_session(self):174 def test_process_response_non_html(self): 82 175 """ 83 Check th at the token is inserted if there is a new session being started176 Check the the post-processor does nothing for content-types not in _HTML_TYPES. 84 177 """ 85 req = self._get_GET_no_session_request() # no session in request 86 resp = self._get_new_session_response() # but new session started 178 req = self._get_GET_no_csrf_cookie_request() 179 CsrfMiddleware().process_view(req, post_form_view, (), {}) 180 resp = post_form_response_non_html() 87 181 resp_content = resp.content # needed because process_response modifies resp 88 182 resp2 = CsrfMiddleware().process_response(req, resp) 89 self.assertNotEqual(resp_content, resp2.content) 90 self._check_token_present(resp2) 183 self.assertEquals(resp_content, resp2.content) 91 184 92 185 def test_process_response_exempt_view(self): 93 186 """ 94 187 Check that no post processing is done for an exempt view 95 188 """ 96 req = self._get_POST_ session_request()97 resp = csrf_exempt( self.get_view())(req)189 req = self._get_POST_csrf_cookie_request() 190 resp = csrf_exempt(post_form_view)(req) 98 191 resp_content = resp.content 99 192 resp2 = CsrfMiddleware().process_response(req, resp) 100 193 self.assertEquals(resp_content, resp2.content) 101 194 102 195 # Check the request processing 103 def test_process_request_no_session (self):196 def test_process_request_no_session_no_csrf_cookie(self): 104 197 """ 105 Check that if n o session is present, the middleware does nothing.106 t o the incoming request.198 Check that if neither a CSRF cookie nor a session cookie are present, 199 the middleware rejects the incoming request. This will stop login CSRF. 107 200 """ 108 req = self._get_POST_no_session_request() 109 req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {}) 201 req = self._get_POST_no_csrf_cookie_request() 202 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 203 self.assertEquals(403, req2.status_code) 204 205 def test_process_request_csrf_cookie_no_token(self): 206 """ 207 Check that if a CSRF cookie is present but no token, the middleware 208 rejects the incoming request. 209 """ 210 req = self._get_POST_csrf_cookie_request() 211 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 212 self.assertEquals(403, req2.status_code) 213 214 def test_process_request_csrf_cookie_and_token(self): 215 """ 216 Check that if both a cookie and a token is present, the middleware lets it through. 217 """ 218 req = self._get_POST_request_with_token() 219 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 110 220 self.assertEquals(None, req2) 111 221 112 def test_process_request_session_ no_token(self):222 def test_process_request_session_cookie_no_csrf_cookie_token(self): 113 223 """ 114 Check that if a session is present but no token, we get a 'forbidden' 224 When no CSRF cookie exists, but the user has a session, check that a token 225 using the session cookie as a legacy CSRF cookie is accepted. 115 226 """ 116 req = self._get_POST_session_request() 117 req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {}) 118 self.assertEquals(HttpResponseForbidden, req2.__class__) 227 orig_secret_key = settings.SECRET_KEY 228 settings.SECRET_KEY = self._secret_key_for_session_test 229 try: 230 req = self._get_POST_session_request_with_token() 231 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 232 self.assertEquals(None, req2) 233 finally: 234 settings.SECRET_KEY = orig_secret_key 119 235 120 def test_process_request_session_ and_token(self):236 def test_process_request_session_cookie_no_csrf_cookie_no_token(self): 121 237 """ 122 Check that if a session is present and a token, the middleware lets it through 238 Check that if a session cookie is present but no token and no CSRF cookie, 239 the request is rejected. 123 240 """ 124 req = self._get_POST_session_request_ with_token()125 req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})126 self.assertEquals( None, req2)241 req = self._get_POST_session_request_no_token() 242 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 243 self.assertEquals(403, req2.status_code) 127 244 128 def test_process_request_ session_no_token_exempt_view(self):245 def test_process_request_csrf_cookie_no_token_exempt_view(self): 129 246 """ 130 Check that if a sessionis present and no token, but the csrf_exempt247 Check that if a CSRF cookie is present and no token, but the csrf_exempt 131 248 decorator has been applied to the view, the middleware lets it through 132 249 """ 133 req = self._get_POST_ session_request()134 req2 = CsrfMiddleware().process_view(req, csrf_exempt( self.get_view()), (), {})250 req = self._get_POST_csrf_cookie_request() 251 req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {}) 135 252 self.assertEquals(None, req2) 136 253 137 254 def test_ajax_exemption(self): 138 255 """ 139 256 Check that AJAX requests are automatically exempted. 140 257 """ 141 req = self._get_POST_ session_request()258 req = self._get_POST_csrf_cookie_request() 142 259 req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' 143 req2 = CsrfMiddleware().process_view(req, self.get_view(), (), {})260 req2 = CsrfMiddleware().process_view(req, post_form_view, (), {}) 144 261 self.assertEquals(None, req2) 262 263 # Tests for the template tag method 264 def test_token_node_no_csrf_cookie(self): 265 """ 266 Check that CsrfTokenNode works when no CSRF cookie is set 267 """ 268 req = self._get_GET_no_csrf_cookie_request() 269 resp = token_view(req) 270 self.assertEquals(u"", resp.content) 271 272 def test_token_node_with_csrf_cookie(self): 273 """ 274 Check that CsrfTokenNode works when a CSRF cookie is set 275 """ 276 req = self._get_GET_csrf_cookie_request() 277 CsrfViewMiddleware().process_view(req, token_view, (), {}) 278 resp = token_view(req) 279 self._check_token_present(resp) 280 281 def test_token_node_with_new_csrf_cookie(self): 282 """ 283 Check that CsrfTokenNode works when a CSRF cookie is created by 284 the middleware (when one was not already present) 285 """ 286 req = self._get_GET_no_csrf_cookie_request() 287 CsrfViewMiddleware().process_view(req, token_view, (), {}) 288 resp = token_view(req) 289 resp2 = CsrfViewMiddleware().process_response(req, resp) 290 csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME] 291 self._check_token_present(resp, csrf_id=csrf_cookie.value) 292 293 def test_response_middleware_without_view_middleware(self): 294 """ 295 Check that CsrfResponseMiddleware finishes without error if the view middleware 296 has not been called, as is the case if a request middleware returns a response. 297 """ 298 req = self._get_GET_no_csrf_cookie_request() 299 resp = post_form_view(req) 300 CsrfMiddleware().process_response(req, resp) 301 302 def test_https_bad_referer(self): 303 """ 304 Test that a POST HTTPS request with a bad referer is rejected 305 """ 306 req = self._get_POST_request_with_token() 307 req._is_secure = True 308 req.META['HTTP_HOST'] = 'www.example.com' 309 req.META['HTTP_REFERER'] = 'https://www.evil.org/somepage' 310 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 311 self.assertNotEqual(None, req2) 312 self.assertEquals(403, req2.status_code) 313 314 def test_https_good_referer(self): 315 """ 316 Test that a POST HTTPS request with a good referer is accepted 317 """ 318 req = self._get_POST_request_with_token() 319 req._is_secure = True 320 req.META['HTTP_HOST'] = 'www.example.com' 321 req.META['HTTP_REFERER'] = 'https://www.example.com/somepage' 322 req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) 323 self.assertEquals(None, req2) -
new file django/contrib/csrf/views.py
diff -r 27807883e23f django/contrib/csrf/views.py
- + 1 from django.http import HttpResponseForbidden 2 from django.template import Context, Template 3 from django.conf import settings 4 5 # We include the template here, since one possible error in setting up CSRF is 6 # that the developer hasn't included the CSRF app in INSTALLED_APPS, in which 7 # case we won't be able to find the template. 8 CSRF_FAILRE_TEMPLATE = """ 9 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 10 <html lang="en"> 11 <head> 12 <meta http-equiv="content-type" content="text/html; charset=utf-8"> 13 <title>403 Forbidden</title> 14 </head> 15 <body> 16 <h1>403 Forbidden</h1> 17 <p>CSRF verification failed. Request aborted.</p> 18 {% if DEBUG %} 19 <h2>Help</h2> 20 {% if reason %} 21 <p>Reason given for failure:</p> 22 <pre> 23 {{ reason }} 24 </pre> 25 {% endif %} 26 27 <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when 28 <a 29 href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-csrf'>Django's 30 CSRF mechanism</a> has not been used correctly. For POST forms, you need to 31 ensure:</p> 32 33 <ul> 34 <li>The view function uses <a 35 href='http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext'><tt>RequestContext</tt></a> 36 for the template, instead of <tt>Context</tt>.</li> 37 38 <li>In the template, there is a <tt>{% templatetag openblock %} csrf_token 39 {% templatetag closeblock %}</tt> template tag inside each POST form that 40 targets an internal URL.</li> 41 </ul> 42 43 <p>You're seeing the help section of this page because you have <code>DEBUG = 44 True</code> in your Django settings file. Change that to <code>False</code>, 45 and only the initial error message will be displayed. </p> 46 47 <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p> 48 49 {% endif %} 50 </body> 51 </html> 52 """ 53 54 def csrf_failure(request, reason=""): 55 """ 56 Default view used when request fails CSRF protection 57 """ 58 t = Template(CSRF_FAILRE_TEMPLATE) 59 c = Context({'DEBUG': settings.DEBUG, 60 'reason': reason}) 61 return HttpResponseForbidden(t.render(c), mimetype='text/html') -
django/contrib/formtools/templates/formtools/form.html
diff -r 27807883e23f django/contrib/formtools/templates/formtools/form.html
a b 4 4 5 5 {% if form.errors %}<h1>Please correct the following errors</h1>{% else %}<h1>Submit</h1>{% endif %} 6 6 7 <form action="" method="post"> 7 <form action="" method="post">{% csrf_token %} 8 8 <table> 9 9 {{ form }} 10 10 </table> -
django/contrib/formtools/templates/formtools/preview.html
diff -r 27807883e23f django/contrib/formtools/templates/formtools/preview.html
a b 15 15 16 16 <p>Security hash: {{ hash_value }}</p> 17 17 18 <form action="" method="post"> 18 <form action="" method="post">{% csrf_token %} 19 19 {% for field in form %}{{ field.as_hidden }} 20 20 {% endfor %} 21 21 <input type="hidden" name="{{ stage_field }}" value="2" /> … … 25 25 26 26 <h1>Or edit it again</h1> 27 27 28 <form action="" method="post"> 28 <form action="" method="post">{% csrf_token %} 29 29 <table> 30 30 {{ form }} 31 31 </table> -
django/contrib/formtools/tests.py
diff -r 27807883e23f django/contrib/formtools/tests.py
a b 147 147 148 148 class WizardClass(wizard.FormWizard): 149 149 def render_template(self, *args, **kw): 150 return ""150 return http.HttpResponse("") 151 151 152 152 def done(self, request, cleaned_data): 153 153 return http.HttpResponse(success_string) 154 154 155 class DummyRequest( object):155 class DummyRequest(http.HttpRequest): 156 156 def __init__(self, POST=None): 157 super(DummyRequest, self).__init__() 157 158 self.method = POST and "POST" or "GET" 158 self.POST = POST 159 if POST is not None: 160 self.POST.update(POST) 161 self.dont_enforce_csrf_checks = True 159 162 160 163 class WizardTests(TestCase): 161 164 def test_step_starts_at_zero(self): -
django/contrib/formtools/wizard.py
diff -r 27807883e23f django/contrib/formtools/wizard.py
a b 14 14 from django.utils.hashcompat import md5_constructor 15 15 from django.utils.translation import ugettext_lazy as _ 16 16 from django.contrib.formtools.utils import security_hash 17 from django.contrib.csrf.decorators import csrf_protect 17 18 18 19 class FormWizard(object): 19 20 # Dictionary of extra template context variables. … … 96 97 self.step = current_step = next_step 97 98 98 99 return self.render(form, request, current_step) 100 __call__ = csrf_protect(__call__) 99 101 100 102 def render(self, form, request, step, context=None): 101 103 "Renders the given Form object, returning an HttpResponse." -
django/template/context.py
diff -r 27807883e23f django/template/context.py
a b 1 1 from django.core.exceptions import ImproperlyConfigured 2 2 from django.utils.importlib import import_module 3 3 4 # Cache of actual callables. 4 5 _standard_context_processors = None 6 # We need the CSRF processor no matter what the user has in their settings, 7 # because otherwise it is a security vulnerability, and we can't afford to leave 8 # this to human error or failure to read migration instructions. 9 _builtin_context_processors = ('django.contrib.csrf.context_processors.csrf',) 5 10 6 11 class ContextPopException(Exception): 7 12 "pop() has been called more times than push()" … … 75 80 global _standard_context_processors 76 81 if _standard_context_processors is None: 77 82 processors = [] 78 for path in settings.TEMPLATE_CONTEXT_PROCESSORS: 83 collect = [] 84 collect.extend(_builtin_context_processors) 85 collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS) 86 for path in collect: 79 87 i = path.rfind('.') 80 88 module, attr = path[:i], path[i+1:] 81 89 try: -
django/template/defaulttags.py
diff -r 27807883e23f django/template/defaulttags.py
a b 37 37 def render(self, context): 38 38 return '' 39 39 40 class CsrfTokenNode(Node): 41 def render(self, context): 42 csrf_token = context.get('csrf_token', None) 43 if csrf_token: 44 if csrf_token == 'NOTPROVIDED': 45 return mark_safe(u"") 46 else: 47 return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % (csrf_token)) 48 else: 49 # It's very probable that the token is missing because of 50 # misconfiguration, so we raise a warning 51 from django.conf import settings 52 if settings.DEBUG: 53 import warnings 54 warnings.warn("A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.") 55 return u'' 56 40 57 class CycleNode(Node): 41 58 def __init__(self, cyclevars, variable_name=None): 42 59 self.cycle_iter = itertools_cycle(cyclevars) … … 523 540 return node 524 541 cycle = register.tag(cycle) 525 542 543 def csrf_token(parser, token): 544 return CsrfTokenNode() 545 register.tag(csrf_token) 546 526 547 def debug(parser, token): 527 548 """ 528 549 Outputs a whole load of debugging information, including the current -
django/test/client.py
diff -r 27807883e23f django/test/client.py
a b 66 66 signals.request_started.send(sender=self.__class__) 67 67 try: 68 68 request = WSGIRequest(environ) 69 # sneaky little hack so that we can easily get round 70 # CsrfViewMiddleware. This makes life easier, and is probably 71 # required for backwards compatibility with external tests against 72 # admin views. 73 request.dont_enforce_csrf_checks = True 69 74 response = self.get_response(request) 70 75 71 76 # Apply response middleware. -
docs/intro/tutorial04.txt
diff -r 27807883e23f docs/intro/tutorial04.txt
a b 21 21 {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} 22 22 23 23 <form action="/polls/{{ poll.id }}/vote/" method="post"> 24 {% csrf_token %} 24 25 {% for choice in poll.choice_set.all %} 25 26 <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> 26 27 <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br /> … … 46 47 * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone 47 48 through its loop 48 49 50 * Since we are creating a POST form (which can have the effect of modifying 51 data), we unfortunately need to worry about Cross Site Request Forgeries. 52 Thankfully, you don't have to worry too hard, because Django comes with 53 very easy-to-use system for protecting against it. In short, all POST 54 forms that are targetted at internal URLs need the ``{% csrf_token %}`` 55 template tag adding. A 'middleware' which is installed by default checks 56 for the presence of the token in the submitted form data. 57 58 The ``{% csrf_token %}`` tag requires information from the request object, which 59 is not normally accessible from within the template context. To fix this, a 60 small adjustment needs to be made to the ``detail`` view, so that it looks like 61 the following:: 62 63 from django.template import RequestContext 64 # ... 65 def detail(request, poll_id): 66 p = get_object_or_404(Poll, pk=poll_id) 67 return render_to_response('polls/detail.html', {'poll': p}, 68 context_instance=RequestContext(request)) 69 70 The details of how this works are explained in the documentation for 71 :ref:`RequestContext <subclassing-context-requestcontext>`. 72 49 73 Now, let's create a Django view that handles the submitted data and does 50 74 something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we 51 75 created a URLconf for the polls application that includes this line:: … … 57 81 from django.shortcuts import get_object_or_404, render_to_response 58 82 from django.http import HttpResponseRedirect 59 83 from django.core.urlresolvers import reverse 84 from django.template import RequestContext 60 85 from mysite.polls.models import Choice, Poll 61 86 # ... 62 87 def vote(request, poll_id): … … 68 93 return render_to_response('polls/detail.html', { 69 94 'poll': p, 70 95 'error_message': "You didn't select a choice.", 71 } )96 }, context_instance=RequestContext(request)) 72 97 else: 73 98 selected_choice.votes += 1 74 99 selected_choice.save() -
docs/ref/contrib/csrf.txt
diff -r 27807883e23f docs/ref/contrib/csrf.txt
a b 7 7 .. module:: django.contrib.csrf 8 8 :synopsis: Protects against Cross Site Request Forgeries 9 9 10 The C srfMiddleware classprovides easy-to-use protection against10 The CSRF middleware and template tag provides easy-to-use protection against 11 11 `Cross Site Request Forgeries`_. This type of attack occurs when a malicious 12 Web site creates a link or form button that is intended to perform some action 13 on your Web site, using the credentials of a logged-in user who is tricked 14 into clicking on the link in their browser. 12 Web site contains a link, a form button or some javascript that is intended to 13 perform some action on your Web site, using the credentials of a logged-in user 14 who visits the malicious site in their browser. A related type of attack, 15 'login CSRF', where an attacking site tricks a user's browser into logging into 16 a site with someone else's credentials, is also covered. 15 17 16 The first defense against CSRF attacks is to ensure that GET requests 17 are side-effect free. POST requests can then be protected by adding this 18 middleware into your list of installed middleware. 18 The first defense against CSRF attacks is to ensure that GET requests are 19 side-effect free. POST requests can then be protected by following the steps 20 below. 21 22 .. versionadded:: 1.2 23 The 'contrib' apps, including the admin, use the functionality described 24 here. Because it is security related, a few things have been added to core 25 functionality to allow this to happen without any required upgrade steps. 19 26 20 27 .. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF 21 28 22 29 How to use it 23 30 ============= 24 31 25 Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to 26 your list of middleware classes, :setting:`MIDDLEWARE_CLASSES`. It needs to process 27 the response after the SessionMiddleware, so must come before it in the 28 list. It also must process the response before things like compression 29 happen to the response, so it must come after GZipMiddleware in the 30 list. 32 .. versionchanged:: 1.2 33 The template tag functionality (the recommended way to use this) was added 34 in version 1.2. The previous method (still available) is described under 35 `Legacy method`_. 31 36 32 The ``CsrfMiddleware`` class is actually composed of two middleware: 33 ``CsrfViewMiddleware`` which performs the checks on incoming requests, 34 and ``CsrfResponseMiddleware`` which performs post-processing of the 35 result. This allows the individual components to be used and/or 36 replaced instead of using ``CsrfMiddleware``. 37 To enable CSRF protection for your views, follow these steps: 37 38 38 .. versionchanged:: 1.1 39 (previous versions of Django did not provide these two components 40 of ``CsrfMiddleware`` as described above) 39 1. Add the middleware 40 ``'django.contrib.csrf.middleware.CsrfViewMiddleware'`` to your list of 41 middleware classes, :setting:`MIDDLEWARE_CLASSES`. (It should come 42 before ``CsrfResponseMiddleware`` if that is being used, and before any 43 view middleware that assume that CSRF attacks have been dealt with.) 44 45 Alternatively, you can use the decorator 46 ``django.contrib.csrf.decorators.csrf_protect`` on particular views you 47 want to protect. This is **not recommended** by itself, since if you 48 forget to use it, you will have a security hole. The 'belt and braces' 49 strategy of using both is fine, and will incur minimal overhead. 50 51 2. In any template that uses a POST form, use the ``csrf_token`` tag inside 52 the ``<form>`` element if the form is for an internal URL, e.g.:: 53 54 <form action="" method="POST">{% csrf_token %} 55 56 This should not be done for POST forms that target external URLs, since 57 that would cause the CSRF token to be leaked, leading to a vulnerability. 58 59 3. In the corresponding view functions, ensure that the 60 ``'django.contrib.csrf.context_processors.csrf'`` context processor is 61 being used. Usually, this can be done in one of two ways: 62 63 1. Use RequestContext, which always uses 64 ``'django.contrib.csrf.context_processors.csrf'`` (no matter what your 65 TEMPLATE_CONTEXT_PROCESSORS setting). If you are using 66 generic views or contrib apps, you are covered already, since these 67 apps use RequestContext throughout. 68 69 2. Manually import and use the processor to generate the CSRF token and 70 add it to the template context. e.g.:: 71 72 from django.contrib.csrf.context_processors import csrf 73 from django.shortcuts import render_to_response 74 75 def my_view(request): 76 c = {} 77 c.update(csrf(request)) 78 # ... view code here 79 return render_to_response("a_template.html", c) 80 81 You may want to write your own ``render_to_response`` wrapper that 82 takes care of this step for you. 83 84 Legacy method 85 ------------- 86 87 In Django 1.1, the template tag did not exist. Instead, a post-processing 88 middleware that re-wrote POST forms to include the CRSF token was used. If you 89 are upgrading a site from version 1.1 or earlier, please read this section and 90 the `Upgrading notes`_ below. The post-processing middleware is still available 91 as ``CsrfResponseMiddleware``, and it can be used by following these steps: 92 93 1. Follow step 1 above to install ``CsrfViewMiddleware``. 94 95 2. Add ``'django.contrib.csrf.middleware.CsrfResponseMiddleware'`` to your 96 :setting:`MIDDLEWARE_CLASSES` setting. 97 98 ``CsrfResponseMiddleware`` needs to process the response before things 99 like compression happen to the response, so it must come after 100 ``GZipMiddleware`` in the list. It also must come after 101 ``CsrfViewMiddleware``. 102 103 Use of the ``CsrfResponseMiddleware`` is not recommended because of the 104 performance hit it imposes, and because of a potential security problem (see 105 below). It can be used as an interim measure until applications have been 106 updated to use the ``{% crsf_token %}`` tag. It is deprecated and will be 107 removed in Django 1.4. 108 109 Django 1.1 and earlier provided a single ``CsrfMiddleware`` class. This is also 110 still available for backwards compatibility. It combines the functions of the 111 two middleware. 112 113 Note also that previous versions of these classes depended on the sessions 114 framework, but this dependency has now been removed, with backward compatibility 115 support so that upgrading will not produce any issues. 116 117 Security of legacy method 118 ~~~~~~~~~~~~~~~~~~~~~~~~~ 119 120 The post-processing ``CsrfResponseMiddleware`` adds the CSRF token to all POST 121 forms (unless the view has been decorated with ``csrf_response_exempt``). If 122 the POST form has an external untrusted site as its target, rather than an 123 internal page, that site will be sent the CSRF token when the form is submitted. 124 Armed with this leaked information, that site will then be able to successfully 125 launch a CSRF attack on your site against that user. The 126 ``@csrf_response_exempt`` decorator can be used to fix this, but only if the 127 page doesn't also contain internal forms that require the token. 128 129 Upgrading notes 130 --------------- 131 132 When upgrading to version 1.2 or later, you may have applications that rely on 133 the old post-processing functionality for CSRF protection, or you may not have 134 enabled any CSRF protection. This section outlines the steps necessary for a 135 smooth upgrade, without having to fix all the applications to use the new 136 template tag method immediately. 137 138 If you have ``CsrfMiddleware`` in your :setting:`MIDDLEWARE_CLASSES`, you will now 139 have a working installation with CSRF protection. It is recommended at this 140 point that you replace ``CsrfMiddleware`` with its two components, 141 ``CsrfViewMiddleware`` and ``CsrfResponseMiddleware`` (in that order). 142 143 If you do not have any of the middleware in your :setting:`MIDDLEWARE_CLASSES`, 144 you will have a working installation but without any CSRF protection for your 145 views (just as you had before). It is strongly recommended to install 146 ``CsrfViewMiddleware`` and ``CsrfResponseMiddleware``, as described above. 147 148 (Note that contrib apps, such as the admin, have been updated to use the 149 ``csrf_protect`` decorator, so that they are secured even if you do not add the 150 ``CsrfViewMiddleware`` to your settings). 151 152 Assuming you have followed the above, all views in your Django site will now be 153 protected by the ``CsrfViewMiddleware``. Contrib apps meet the requirements 154 imposed by the ``CsrfViewMiddleware`` using the template tag, and other 155 applications in your project will meet its requirements by virtue of the 156 ``CsrfResponseMiddleware``. 157 158 The next step is to update all your applications to use the template tag, as 159 described in `How to use it`_, steps 2-3. This can be done as soon as is 160 practical. Any applications that are updated will now require Django 1.2 or 161 later, since they will use the CSRF template tag library which was not available 162 in earlier versions. 163 164 Finally, once all applications are upgraded, ``CsrfResponseMiddleware`` can be 165 removed from your settings. 166 167 While ``CsrfResponseMiddleware`` is still in use, the ``csrf_response_exempt`` 168 decorator, described in `Exceptions`_, may be useful. The post-processing 169 middleware imposes a performance hit and a potential vulnerability, and any 170 views that have been upgraded to use the new template tag method no longer need 171 it. 41 172 42 173 Exceptions 43 174 ---------- 44 175 45 176 .. versionadded:: 1.1 46 177 47 To manually exclude a view function from being handled by the48 CsrfMiddleware, you can use the ``csrf_exempt`` decorator, found in 49 the``django.contrib.csrf.middleware`` module. For example::178 To manually exclude a view function from being handled by either of the two CSRF 179 middleware, you can use the ``csrf_exempt`` decorator, found in the 180 ``django.contrib.csrf.middleware`` module. For example:: 50 181 51 182 from django.contrib.csrf.middleware import csrf_exempt 52 183 … … 54 185 return HttpResponse('Hello world') 55 186 my_view = csrf_exempt(my_view) 56 187 57 Like the middleware itself, the ``csrf_exempt`` decorator is composed58 of two parts: a ``csrf_view_exempt`` decorator and a 59 ``csrf_response_exempt`` decorator, found in the same module. These 60 disable the view protection mechanism (``CsrfViewMiddleware``) and the 61 response post-processing (``CsrfResponseMiddleware``) respectively. 62 They can be used individually ifrequired.188 Like the middleware, the ``csrf_exempt`` decorator is composed of two parts: a 189 ``csrf_view_exempt`` decorator and a ``csrf_response_exempt`` decorator, found 190 in the same module. These disable the view protection mechanism 191 (``CsrfViewMiddleware``) and the response post-processing 192 (``CsrfResponseMiddleware``) respectively. They can be used individually if 193 required. 63 194 64 You don't have to worry about doing this for most AJAX views. Any 65 request sent with "X-Requested-With: XMLHttpRequest" is automatically 66 exempt. (See the next section.) 195 You don't have to worry about doing this for most AJAX views. Any request sent 196 with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the next 197 section.) 198 199 Subdomains 200 ---------- 201 202 By default, CSRF cookies are specific to the subdomain they are set for. This 203 means that a form served from one subdomain (e.g. server1.example.com) will not 204 be able to have a target on another subdomain (e.g. server2.example.com). This 205 restriction can be removed by setting :setting:`CSRF_COOKIE_DOMAIN` to be 206 something like ``".example.com"``. 207 208 Please note that, with or without use of this setting, this CSRF protection 209 mechanism is not safe against cross-subdomain attacks -- see `Limitations`_. 210 211 Rejected requests 212 ================= 213 214 By default, a '403 Forbidden' response is sent to the user if an incoming 215 request fails the checks performed by ``CsrfViewMiddleware``. This should 216 usually only be seen when there is a genuine Cross Site Request Forgery, or 217 when, due to a programming error, the CSRF token has not been included with a 218 POST form. 219 220 No logging is done, and the error message is not very friendly, so you may want 221 to provide your own page for handling this condition. To do this, simply set 222 the :setting:`CSRF_FAILURE_VIEW` setting to a dotted path to your own view 223 function, which should have the following signature:: 224 225 def csrf_failure(request, reason="") 226 227 where ``reason`` is a short message (intended for developers or logging, not for 228 end users) indicating the reason the request was rejected. 67 229 68 230 How it works 69 231 ============ 70 232 71 CsrfMiddleware does twothings:233 The CSRF protection is based on the following things: 72 234 73 1. It modifies outgoing requests by adding a hidden form field to all 74 'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is 75 a hash of the session ID plus a secret. If there is no session ID set, 76 this modification of the response isn't done, so there is very little 77 performance penalty for those requests that don't have a session. 78 (This is done by ``CsrfResponseMiddleware``). 235 1. A CSRF cookie that is set to a random value (a session independent nonce, as 236 it is called), which other sites will not have access to. 79 237 80 2. On all incoming POST requests that have the session cookie set, it 81 checks that the 'csrfmiddlewaretoken' is present and correct. If it82 isn't, the user will get a 403 error. (This is done by83 ``CsrfViewMiddleware``)238 This cookie is set by ``CsrfViewMiddleware``. It is meant to be permanent, 239 but since there is no way to set a cookie that never expires, it is sent with 240 every response that has called ``django.contrib.csrf.middleware.get_token()`` 241 (the function used internally to retrieve the CSRF token). 84 242 85 This ensures that only forms that have originated from your Web site 86 can be used to POST data back. 243 2. A hidden form field with the name 'csrfmiddlewaretoken' present in all 244 outgoing POST forms. The value of this field is the value of the CSRF 245 cookie. 246 247 This part is done by the template tag (and with the legacy method, it is done 248 by ``CsrfResponseMiddleware``). 249 250 3. For all incoming POST requests, a CSRF cookie must be present, and the 251 'csrfmiddlewaretoken' field must be present and correct. If it isn't, the 252 user will get a 403 error. 253 254 This check is done by ``CsrfViewMiddleware``. 255 256 4. In addition, for HTTPS requests, strict referer checking is done by 257 ``CsrfViewMiddleware``. This is necessary to address a Man-In-The-Middle 258 attack that is possible under HTTPS when using a session independent nonce, 259 due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted 260 by clients that are talking to a site under HTTPS. (Referer checking is not 261 done for HTTP requests, because under HTTP, the presence of the Referer 262 header is not reliable enough.) 263 264 This ensures that only forms that have originated from your Web site can be used 265 to POST data back. 87 266 88 267 It deliberately only targets HTTP POST requests (and the corresponding POST 89 forms). GET requests ought never to have any potentially dangerous side 90 effects (see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a 91 CSRF attack with a GET request ought to be harmless. 92 93 POST requests that are not accompanied by a session cookie are not protected, 94 but they do not need to be protected, since the 'attacking' Web site 95 could make these kind of requests anyway. 96 97 The Content-Type is checked before modifying the response, and only 98 pages that are served as 'text/html' or 'application/xml+xhtml' 99 are modified. 268 forms). GET requests ought never to have any potentially dangerous side effects 269 (see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a CSRF attack with a GET 270 request ought to be harmless. 100 271 101 272 The middleware tries to be smart about requests that come in via AJAX. Many 102 273 JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP header; 103 274 these requests are detected and automatically *not* handled by this middleware. 104 275 We can do this safely because, in the context of a browser, the header can only 105 276 be added by using ``XMLHttpRequest``, and browsers already implement a 106 same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you 107 don't trust content within the same domain or subdomains.) 277 same-domain policy for ``XMLHttpRequest``. For the more recent browsers that 278 relax this same-domain policy, custom headers like "X-Requested-With" are only 279 allowed after the browser has done a 'preflight' check to the server to see if 280 the cross-domain request is allowed, using a strictly 'opt in' mechanism, so 281 this is still safe. 108 282 283 ``CsrfResponseMiddleware`` checks the Content-Type before modifying the 284 response, and only pages that are served as 'text/html' or 285 'application/xml+xhtml' are modified. 109 286 110 287 .. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html 111 288 289 Caching 290 ======= 291 292 If the ``csrf_token`` template tag is used by a template (or the ``get_token`` 293 function is called some other way), ``CsrfViewMiddleware`` will add a cookie and 294 a ``Vary: Cookie`` header to the response. Similarly, 295 ``CsrfResponseMiddleware`` will send the ``Vary: Cookie`` header if it inserted 296 a token. This means that these middleware will play well with the cache 297 middleware if it is used as instructed (``UpdateCacheMiddleware`` goes before 298 all other middleware). 299 300 However, if you use cache decorators on individual views, the CSRF middleware 301 will not yet have been able to set the Vary header. In this case, on any views 302 that will require a CSRF token to be inserted you should use the 303 :func:`django.views.decorators.vary.vary_on_cookie` decorator first:: 304 305 from django.views.decorators.cache import cache_page 306 from django.views.decorators.vary import vary_on_cookie 307 308 def my_view(request): 309 ... 310 311 my_view = cache_page(vary_on_cookie(my_view), 60 * 15) 312 313 Or, using Python 2.4's decorator syntax:: 314 315 @cache_page(60 * 15) 316 @vary_on_cookie 317 def my_view(request): 318 ... 319 320 Testing 321 ======= 322 323 The ``CsrfViewMiddleware`` will usually be a big hindrance to testing view 324 functions, due to the need for the CSRF token which must be sent with every POST 325 request. For this reason, Django's HTTP client for tests has been modified to 326 set a flag on requests which relaxes the middleware and the ``csrf_protect`` 327 decorator so that they no longer rejects requests. In every other respect 328 (e.g. sending cookies etc.), they behave the same. 329 112 330 Limitations 113 331 =========== 114 332 115 CsrfMiddleware requires Django's session framework to work. If you have 116 a custom authentication system that manually sets cookies and the like, 117 it won't help you. 333 Subdomains within a site will be able to set cookies on the client for the whole 334 domain. By setting the cookie and using a corresponding token, subdomains will 335 be able to circumvent the CSRF protection. The only way to avoid this is to 336 ensure that subdomains are controlled by trusted users (or, are at least unable 337 to set cookies). Note that even without CSRF, there are other vulnerabilities, 338 such as session fixation, that make giving subdomains to untrusted parties a bad 339 idea, and these vulnerabilities cannot easily be fixed with current browsers. 118 340 119 If you r app creates HTML pages and forms in some unusual way, (e.g.120 it sends fragments of HTML in JavaScript document.write statements) 121 you might bypass the filter that adds the hidden field to the form, 122 in which case form submission will always fail. It may still be possible 123 to use the middleware, provided you can find some way to get the 124 CSRF token and ensure that is included when your form is submitted.341 If you are using ``CsrfResponseMiddleware`` and your app creates HTML pages and 342 forms in some unusual way, (e.g. it sends fragments of HTML in JavaScript 343 document.write statements) you might bypass the filter that adds the hidden 344 field to the form, in which case form submission will always fail. You should 345 use the template tag or :meth:`django.contrib.csrf.middleware.get_token` to get 346 the CSRF token and ensure it is included when your form is submitted. -
docs/ref/contrib/formtools/form-wizard.txt
diff -r 27807883e23f docs/ref/contrib/formtools/form-wizard.txt
a b 177 177 178 178 {% block content %} 179 179 <p>Step {{ step }} of {{ step_count }}</p> 180 <form action="." method="post"> 180 <form action="." method="post">{% csrf_token %} 181 181 <table> 182 182 {{ form }} 183 183 </table> -
docs/ref/settings.txt
diff -r 27807883e23f docs/ref/settings.txt
a b 144 144 The default number of seconds to cache a page when the caching middleware or 145 145 ``cache_page()`` decorator is used. 146 146 147 .. setting:: CSRF_COOKIE_NAME 148 149 CSRF_COOKIE_NAME 150 ---------------- 151 Default: ``'csrftoken'`` 152 153 The name of the cookie to use for the CSRF authentication token. This can be whatever you 154 want. See :ref:`ref-contrib-csrf`. 155 156 .. setting:: CSRF_COOKIE_DOMAIN 157 158 CSRF_COOKIE_DOMAIN 159 ------------------ 160 161 Default: ``None`` 162 163 The domain to be used when setting the CSRF cookie. This can be useful for 164 allowing cross-subdomain requests to be exluded from the normal cross site 165 request forgery protection. It should be set to a string such as 166 ``".lawrence.com"`` to allow a POST request from a form on one subdomain to be 167 accepted by accepted by a view served from another subdomain. 168 169 .. setting:: CSRF_FAILURE_VIEW 170 171 CSRF_FAILURE_VIEW 172 ----------------- 173 174 Default: ``'django.contrib.csrf.views.csrf_failure'`` 175 176 A dotted path to the view function to be used when an incoming request 177 is rejected by the CSRF protection. The function should have this signature:: 178 179 def csrf_failure(request, reason="") 180 181 where ``reason`` is a short message (intended for developers or logging, not for 182 end users) indicating the reason the request was rejected. See 183 :ref:`ref-contrib-csrf`. 184 147 185 .. setting:: DATABASE_ENGINE 148 186 149 187 DATABASE_ENGINE … … 751 789 752 790 ('django.middleware.common.CommonMiddleware', 753 791 'django.contrib.sessions.middleware.SessionMiddleware', 792 'django.contrib.csrf.middleware.CsrfViewMiddleware', 754 793 'django.contrib.auth.middleware.AuthenticationMiddleware',) 755 794 756 795 A tuple of middleware classes to use. See :ref:`topics-http-middleware`. -
docs/ref/templates/api.txt
diff -r 27807883e23f docs/ref/templates/api.txt
a b 313 313 "django.core.context_processors.i18n", 314 314 "django.core.context_processors.media") 315 315 316 .. versionadded:: 1.2 317 In addition to these, ``RequestContext`` always uses 318 ``'django.contrib.csrf.context_processors.csrf'``. This is a security 319 related context processor required by the admin and other contrib apps, and, 320 in case of accidental misconfiguration, it is deliberately hardcoded in and 321 cannot be turned off by the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting. 322 316 323 Each processor is applied in order. That means, if one processor adds a 317 324 variable to the context and a second processor adds a variable with the same 318 325 name, the second will override the first. The default processors are explained … … 404 411 ``RequestContext`` will contain a variable ``MEDIA_URL``, providing the 405 412 value of the :setting:`MEDIA_URL` setting. 406 413 414 django.contrib.csrf.context_processors.csrf 415 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 416 417 .. versionadded:: 1.2 418 419 This processor adds a token that is needed by the ``csrf_token`` template tag 420 for protection against :ref:`Cross Site Request Forgeries <ref-contrib-csrf>`. 421 407 422 django.core.context_processors.request 408 423 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 409 424 -
docs/ref/templates/builtins.txt
diff -r 27807883e23f docs/ref/templates/builtins.txt
a b 53 53 54 54 .. templatetag:: cycle 55 55 56 csrf_token 57 ~~~~~~~~~~ 58 59 .. versionadded:: 1.2 60 61 This is described in the documentation for :ref:`Cross Site Request Forgeries <ref-contrib-csrf>`. 62 56 63 cycle 57 64 ~~~~~ 58 65 -
docs/topics/auth.txt
diff -r 27807883e23f docs/topics/auth.txt
a b 767 767 <p>Your username and password didn't match. Please try again.</p> 768 768 {% endif %} 769 769 770 <form method="post" action="{% url django.contrib.auth.views.login %}"> 770 <form method="post" action="{% url django.contrib.auth.views.login %}">{% csrf_token %} 771 771 <table> 772 772 <tr> 773 773 <td>{{ form.username.label_tag }}</td> -
docs/topics/http/middleware.txt
diff -r 27807883e23f docs/topics/http/middleware.txt
a b 29 29 MIDDLEWARE_CLASSES = ( 30 30 'django.middleware.common.CommonMiddleware', 31 31 'django.contrib.sessions.middleware.SessionMiddleware', 32 'django.contrib.csrf.middleware.CsrfViewMiddleware', 32 33 'django.contrib.auth.middleware.AuthenticationMiddleware', 33 34 ) 34 35 -
tests/regressiontests/admin_views/tests.py
diff -r 27807883e23f tests/regressiontests/admin_views/tests.py
a b 885 885 # 4 action inputs (3 regular checkboxes, 1 checkbox to select all) 886 886 # main form submit button = 1 887 887 # search field and search submit button = 2 888 # 6 + 2 + 1 + 2 = 11 inputs 889 self.failUnlessEqual(response.content.count("<input"), 15) 888 # CSRF field = 1 889 # 6 + 2 + 4 + 1 + 2 + 1 = 16 inputs 890 self.failUnlessEqual(response.content.count("<input"), 16) 890 891 # 1 select per object = 3 selects 891 892 self.failUnlessEqual(response.content.count("<select"), 4) 892 893