web/lib/django/contrib/csrf/middleware.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 """
     1 from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware, CsrfResponseMiddleware
     2 Cross Site Request Forgery Middleware.
     2 from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, csrf_response_exempt
     3 
     3 
     4 This module provides a middleware that implements protection
     4 import warnings
     5 against request forgeries from other sites.
     5 warnings.warn("This import for CSRF functionality is deprecated.  Please use django.middleware.csrf for the middleware and django.views.decorators.csrf for decorators.",
     6 """
     6               PendingDeprecationWarning
     7 
     7               )
     8 import re
       
     9 import itertools
       
    10 try:
       
    11     from functools import wraps
       
    12 except ImportError:
       
    13     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
       
    14 
       
    15 from django.conf import settings
       
    16 from django.http import HttpResponseForbidden
       
    17 from django.utils.hashcompat import md5_constructor
       
    18 from django.utils.safestring import mark_safe
       
    19 
       
    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 _POST_FORM_RE = \
       
    23     re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
       
    24 
       
    25 _HTML_TYPES = ('text/html', 'application/xhtml+xml')
       
    26 
       
    27 def _make_token(session_id):
       
    28     return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
       
    29 
       
    30 class CsrfViewMiddleware(object):
       
    31     """
       
    32     Middleware that requires a present and correct csrfmiddlewaretoken
       
    33     for POST requests that have an active session.
       
    34     """
       
    35     def process_view(self, request, callback, callback_args, callback_kwargs):
       
    36         if request.method == 'POST':
       
    37             if getattr(callback, 'csrf_exempt', False):
       
    38                 return None
       
    39 
       
    40             if request.is_ajax():
       
    41                 return None
       
    42 
       
    43             try:
       
    44                 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
       
    45             except KeyError:
       
    46                 # No session, no check required
       
    47                 return None
       
    48 
       
    49             csrf_token = _make_token(session_id)
       
    50             # check incoming token
       
    51             try:
       
    52                 request_csrf_token = request.POST['csrfmiddlewaretoken']
       
    53             except KeyError:
       
    54                 return HttpResponseForbidden(_ERROR_MSG)
       
    55 
       
    56             if request_csrf_token != csrf_token:
       
    57                 return HttpResponseForbidden(_ERROR_MSG)
       
    58 
       
    59         return None
       
    60 
       
    61 class CsrfResponseMiddleware(object):
       
    62     """
       
    63     Middleware that post-processes a response to add a
       
    64     csrfmiddlewaretoken if the response/request have an active
       
    65     session.
       
    66     """
       
    67     def process_response(self, request, response):
       
    68         if getattr(response, 'csrf_exempt', False):
       
    69             return response
       
    70 
       
    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:
       
    92 
       
    93             # ensure we don't add the 'id' attribute twice (HTML validity)
       
    94             idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
       
    95                                             itertools.repeat(''))
       
    96             def add_csrf_field(match):
       
    97                 """Returns the matched <form> tag plus the added <input> element"""
       
    98                 return mark_safe(match.group() + "<div style='display:none;'>" + \
       
    99                 "<input type='hidden' " + idattributes.next() + \
       
   100                 " name='csrfmiddlewaretoken' value='" + csrf_token + \
       
   101                 "' /></div>")
       
   102 
       
   103             # Modify any POST forms
       
   104             response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
       
   105         return response
       
   106 
       
   107 class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware):
       
   108     """Django middleware that adds protection against Cross Site
       
   109     Request Forgeries by adding hidden form fields to POST forms and
       
   110     checking requests for the correct value.
       
   111 
       
   112     In the list of middlewares, SessionMiddleware is required, and
       
   113     must come after this middleware.  CsrfMiddleWare must come after
       
   114     compression middleware.
       
   115 
       
   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.
       
   120 
       
   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
       
   128 
       
   129 def csrf_response_exempt(view_func):
       
   130     """
       
   131     Modifies a view function so that its response is exempt
       
   132     from the post-processing of the CSRF middleware.
       
   133     """
       
   134     def wrapped_view(*args, **kwargs):
       
   135         resp = view_func(*args, **kwargs)
       
   136         resp.csrf_exempt = True
       
   137         return resp
       
   138     return wraps(view_func)(wrapped_view)
       
   139 
       
   140 def csrf_view_exempt(view_func):
       
   141     """
       
   142     Marks a view function as being exempt from CSRF view protection.
       
   143     """
       
   144     # We could just do view_func.csrf_exempt = True, but decorators
       
   145     # are nicer if they don't have side-effects, so we return a new
       
   146     # function.
       
   147     def wrapped_view(*args, **kwargs):
       
   148         return view_func(*args, **kwargs)
       
   149     wrapped_view.csrf_exempt = True
       
   150     return wraps(view_func)(wrapped_view)
       
   151 
       
   152 def csrf_exempt(view_func):
       
   153     """
       
   154     Marks a view function as being exempt from the CSRF checks
       
   155     and post processing.
       
   156 
       
   157     This is the same as using both the csrf_view_exempt and
       
   158     csrf_response_exempt decorators.
       
   159     """
       
   160     return csrf_response_exempt(csrf_view_exempt(view_func))