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