~ubuntu-branches/ubuntu/quantal/python-django/quantal-security

« back to all changes in this revision

Viewing changes to django/middleware/csrf.py

  • Committer: Bazaar Package Importer
  • Author(s): Jamie Strandboge
  • Date: 2010-10-12 11:34:35 UTC
  • mfrom: (1.2.7 upstream)
  • mto: This revision was merged to the branch mainline in revision 30.
  • Revision ID: james.westby@ubuntu.com-20101012113435-9lnsrh0i3mxozbt0
Tags: upstream-1.2.3
ImportĀ upstreamĀ versionĀ 1.2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
    randrange = random.randrange
28
28
_MAX_CSRF_KEY = 18446744073709551616L     # 2 << 63
29
29
 
 
30
REASON_NO_REFERER = "Referer checking failed - no Referer."
 
31
REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
 
32
REASON_NO_COOKIE = "No CSRF or session cookie."
 
33
REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
 
34
REASON_BAD_TOKEN = "CSRF token missing or incorrect."
 
35
 
 
36
 
30
37
def _get_failure_view():
31
38
    """
32
39
    Returns the view to be used for CSRF rejections
33
40
    """
34
41
    return get_callable(settings.CSRF_FAILURE_VIEW)
35
42
 
 
43
 
36
44
def _get_new_csrf_key():
37
45
    return md5_constructor("%s%s"
38
46
                % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
39
47
 
 
48
 
40
49
def _make_legacy_session_token(session_id):
41
50
    return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
42
51
 
 
52
 
43
53
def get_token(request):
44
54
    """
45
 
    Returns the the CSRF token required for a POST form.
 
55
    Returns the the CSRF token required for a POST form. The token is an
 
56
    alphanumeric value.
46
57
 
47
58
    A side effect of calling this function is to make the the csrf_protect
48
59
    decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
52
63
    request.META["CSRF_COOKIE_USED"] = True
53
64
    return request.META.get("CSRF_COOKIE", None)
54
65
 
 
66
 
 
67
def _sanitize_token(token):
 
68
    # Allow only alphanum, and ensure we return a 'str' for the sake of the post
 
69
    # processing middleware.
 
70
    token = re.sub('[^a-zA-Z0-9]', '', str(token.decode('ascii', 'ignore')))
 
71
    if token == "":
 
72
        # In case the cookie has been truncated to nothing at some point.
 
73
        return _get_new_csrf_key()
 
74
    else:
 
75
        return token
 
76
 
 
77
 
55
78
class CsrfViewMiddleware(object):
56
79
    """
57
80
    Middleware that requires a present and correct csrfmiddlewaretoken
62
85
    tag.
63
86
    """
64
87
    def process_view(self, request, callback, callback_args, callback_kwargs):
65
 
        if getattr(callback, 'csrf_exempt', False):
66
 
            return None
67
 
 
68
88
        if getattr(request, 'csrf_processing_done', False):
69
89
            return None
70
90
 
80
100
        # request, so it's available to the view.  We'll store it in a cookie when
81
101
        # we reach the response.
82
102
        try:
83
 
            request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME]
 
103
            # In case of cookies from untrusted sources, we strip anything
 
104
            # dangerous at this point, so that the cookie + token will have the
 
105
            # same, sanitized value.
 
106
            request.META["CSRF_COOKIE"] = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME])
84
107
            cookie_is_new = False
85
108
        except KeyError:
86
109
            # No cookie, so create one.  This will be sent with the next
90
113
            # place of a CSRF cookie for this request only.
91
114
            cookie_is_new = True
92
115
 
 
116
        # Wait until request.META["CSRF_COOKIE"] has been manipulated before
 
117
        # bailing out, so that get_token still works
 
118
        if getattr(callback, 'csrf_exempt', False):
 
119
            return None
 
120
 
93
121
        if request.method == 'POST':
94
122
            if getattr(request, '_dont_enforce_csrf_checks', False):
95
123
                # Mechanism to turn off CSRF checks for test suite.  It comes after
127
155
                # Strict referer checking for HTTPS
128
156
                referer = request.META.get('HTTP_REFERER')
129
157
                if referer is None:
130
 
                    return reject("Referer checking failed - no Referer.")
 
158
                    return reject(REASON_NO_REFERER)
131
159
 
132
160
                # The following check ensures that the referer is HTTPS,
133
161
                # the domains match and the ports match.  This might be too strict.
134
162
                good_referer = 'https://%s/' % request.get_host()
135
163
                if not referer.startswith(good_referer):
136
 
                    return reject("Referer checking failed - %s does not match %s." %
 
164
                    return reject(REASON_BAD_REFERER %
137
165
                                  (referer, good_referer))
138
166
 
139
167
            # If the user didn't already have a CSRF cookie, then fall back to
148
176
                    # No CSRF cookie and no session cookie. For POST requests,
149
177
                    # we insist on a CSRF cookie, and in this way we can avoid
150
178
                    # all CSRF attacks, including login CSRF.
151
 
                    return reject("No CSRF or session cookie.")
 
179
                    return reject(REASON_NO_COOKIE)
152
180
            else:
153
181
                csrf_token = request.META["CSRF_COOKIE"]
154
182
 
157
185
            if request_csrf_token != csrf_token:
158
186
                if cookie_is_new:
159
187
                    # probably a problem setting the CSRF cookie
160
 
                    return reject("CSRF cookie not set.")
 
188
                    return reject(REASON_NO_CSRF_COOKIE)
161
189
                else:
162
 
                    return reject("CSRF token missing or incorrect.")
 
190
                    return reject(REASON_BAD_TOKEN)
163
191
 
164
192
        return accept()
165
193
 
185
213
        response.csrf_processing_done = True
186
214
        return response
187
215
 
 
216
 
188
217
class CsrfResponseMiddleware(object):
189
218
    """
190
219
    DEPRECATED
235
264
                del response['ETag']
236
265
        return response
237
266
 
 
267
 
238
268
class CsrfMiddleware(object):
239
269
    """
240
270
    Django middleware that adds protection against Cross Site
262
292
    def process_view(self, request, callback, callback_args, callback_kwargs):
263
293
        return self.view_middleware.process_view(request, callback, callback_args,
264
294
                                                 callback_kwargs)
265