app/django/contrib/csrf/middleware.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 """
       
     2 Cross Site Request Forgery Middleware.
       
     3 
       
     4 This module provides a middleware that implements protection
       
     5 against request forgeries from other sites. 
       
     6 
       
     7 """
       
     8 from django.conf import settings
       
     9 from django.http import HttpResponseForbidden
       
    10 from django.utils.safestring import mark_safe
       
    11 import md5
       
    12 import re
       
    13 import itertools
       
    14 
       
    15 _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>')
       
    16 
       
    17 _POST_FORM_RE = \
       
    18     re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
       
    19     
       
    20 _HTML_TYPES = ('text/html', 'application/xhtml+xml')    
       
    21 
       
    22 def _make_token(session_id):
       
    23     return md5.new(settings.SECRET_KEY + session_id).hexdigest()
       
    24 
       
    25 class CsrfMiddleware(object):
       
    26     """Django middleware that adds protection against Cross Site
       
    27     Request Forgeries by adding hidden form fields to POST forms and 
       
    28     checking requests for the correct value.  
       
    29     
       
    30     In the list of middlewares, SessionMiddleware is required, and must come 
       
    31     after this middleware.  CsrfMiddleWare must come after compression 
       
    32     middleware.
       
    33    
       
    34     If a session ID cookie is present, it is hashed with the SECRET_KEY 
       
    35     setting to create an authentication token.  This token is added to all 
       
    36     outgoing POST forms and is expected on all incoming POST requests that 
       
    37     have a session ID cookie.
       
    38     
       
    39     If you are setting cookies directly, instead of using Django's session 
       
    40     framework, this middleware will not work.
       
    41     """
       
    42     
       
    43     def process_request(self, request):
       
    44         if request.method == 'POST':
       
    45             try:
       
    46                 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
       
    47             except KeyError:
       
    48                 # No session, no check required
       
    49                 return None
       
    50 
       
    51             csrf_token = _make_token(session_id)
       
    52             # check incoming token
       
    53             try:
       
    54                 request_csrf_token = request.POST['csrfmiddlewaretoken']
       
    55             except KeyError:
       
    56                 return HttpResponseForbidden(_ERROR_MSG)
       
    57             
       
    58             if request_csrf_token != csrf_token:
       
    59                 return HttpResponseForbidden(_ERROR_MSG)
       
    60                 
       
    61         return None
       
    62 
       
    63     def process_response(self, request, response):
       
    64         csrf_token = None
       
    65         try:
       
    66             cookie = response.cookies[settings.SESSION_COOKIE_NAME]
       
    67             csrf_token = _make_token(cookie.value)
       
    68         except KeyError:
       
    69             # No outgoing cookie to set session, but 
       
    70             # a session might already exist.
       
    71             try:
       
    72                 session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
       
    73                 csrf_token = _make_token(session_id)
       
    74             except KeyError:
       
    75                 # no incoming or outgoing cookie
       
    76                 pass
       
    77             
       
    78         if csrf_token is not None and \
       
    79                 response['Content-Type'].split(';')[0] in _HTML_TYPES:
       
    80             
       
    81             # ensure we don't add the 'id' attribute twice (HTML validity)
       
    82             idattributes = itertools.chain(("id='csrfmiddlewaretoken'",), 
       
    83                                             itertools.repeat(''))
       
    84             def add_csrf_field(match):
       
    85                 """Returns the matched <form> tag plus the added <input> element"""
       
    86                 return mark_safe(match.group() + "<div style='display:none;'>" + \
       
    87                 "<input type='hidden' " + idattributes.next() + \
       
    88                 " name='csrfmiddlewaretoken' value='" + csrf_token + \
       
    89                 "' /></div>")
       
    90 
       
    91             # Modify any POST forms
       
    92             response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
       
    93         return response