Skip to content

session_csrf accesses POST on non-POST requests, which breaks piston #12

@scjody

Description

@scjody

When session_csrf is used in conjunction with piston to protect a PUT request, things break:

File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/views/decorators/vary.py", line 19, in inner_func
  response = func(*args, **kwargs)
File "/home/scjody/pwbank/src/hg/web-banking/src/trustcentric/resource.py", line 101, in __call__ 
  coerce_put_post(request)
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/piston/utils.py", line 144, in coerce_put_post
  request._load_post_and_files()
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/http/__init__.py", line 360, in _load_post_and_files
  self._post, self._files = self.parse_file_upload(self.META, data)
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/http/__init__.py", line 317, in parse_file_upload
  warning = "You cannot alter upload handlers after the upload has been processed."
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/http/__init__.py", line 302, in _set_upload_handlers
  raise AttributeError("You cannot set the upload handlers after the upload has been processed.")
AttributeError: You cannot set the upload handlers after the upload has been processed.

This is because of the way Piston works around Django's lack of PUT support: it sets request.method to POST, calls request._load_post_and_files(), and then sets request.method back to PUT. This works fine unless session_csrf is being used, in which case _load_post_and_files() fails with the error above.

I can't think of a good way to fix Piston. A fairly gross way to work around it in session_csrf is to do:

--- a/session_csrf/__init__.py
+++ b/session_csrf/__init__.py
@@ -87,7 +87,9 @@ class CsrfMiddleware(object):

         # Try to get the token from the POST and fall back to looking at the
         # X-CSRFTOKEN header.
-        user_token = request.POST.get('csrfmiddlewaretoken', '')
+        user_token = ''
+        if request.method == 'POST':
+            user_token = request.POST.get('csrfmiddlewaretoken', '')
         if user_token == '':
             user_token = request.META.get('HTTP_X_CSRFTOKEN', '')

This will prevent any request that sets a "csrfmiddlewaretoken" field in a PUT rather than using an X-CSRFToken header from passing CSRF checks, but that wasn't going to work anyway with the current session_csrf (it only checks POST, which will be empty.)

Given that Django's PUT support is lacking anyway - anyone doing a PUT is probably using Piston or Tastypie, which uses _load_post_and_files too - is this the best we can do, or can someone think of a better way?

Should I submit a pull request with this fix given that it's better than what session_csrf currently does?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions