Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#14182 closed (fixed)

CsrfViewMiddleware makes modification of the upload handlers impossible

Reported by: dc Owned by: lukeplant
Component: File uploads/storage Version: 1.2
Severity: Keywords: csrf upload_handlers
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

CsrfViewMiddleware accesses request.POST and makes modification of the upload handlers on a per-request basis impossible.

Currently it's impossible to simultaneously use per-request upload handlers and csrf protection. At least this must be documented.

See also: http://docs.djangoproject.com/en/1.2/topics/http/file-uploads/#modifying-upload-handlers-on-the-fly

Steps to reproduce

Put attached upload_test.py in PYTHONPATH.

Start new default django project:

$ django-admin.py startproject project

Modify project/urls.py:

urlpatterns = patterns('',
    (r'^upload/$', 'upload_test.upload_file'),
)

Open browser at /upload/ and submit form.

Actual Results

AttributeError at /upload/

You cannot alter upload handlers after the upload has been processed.

Expected Results

Normal form handling in upload_file() view.

Attachments (1)

upload_test.py (1.3 KB) - added by dc 5 years ago.
Test file

Download all attachments as: .zip

Change History (5)

Changed 5 years ago by dc

Test file

comment:1 Changed 5 years ago by lukeplant

  • Needs documentation unset
  • Needs tests unset
  • Owner changed from nobody to lukeplant
  • Patch needs improvement unset
  • Status changed from new to assigned

Hmmm, tricky, I can't think of a nice way to get this to work automatically. If you use csrf_exempt can you work around this? Something like this:

from django.views.decorators.csrf import csrf_exempt, csrf_protect  ## Added

@csrf_exempt                         ## Added
def upload_file(request):
    request.upload_handlers.insert(0, DummyUploadHandler())
    return upload_file_real(request) ## Added

@csrf_protect                        ## Added
def upload_file_real(request)        ## Added

    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            template = Template('''
                <!DOCTYPE html>
                <title></title>
                <h1>Done</h1>
            ''')
            context = RequestContext(request)
            return HttpResponse(template.render(context))
    else:
        form = UploadFileForm()

    template = Template('''
        <!DOCTYPE html>
        <title></title>
        <form action="." enctype="multipart/form-data" method="post">
            {% csrf_token %}
            {{ form }}
            <input type="submit">
        </form>
    ''')
    context = RequestContext(request, {'form': form})
    return HttpResponse(template.render(context))

Does that work? If it does, would you mind coming up with a patch for the documentation?

comment:2 Changed 5 years ago by laurikari

Replying to lukeplant:

Using @csrf_exempt and @csrf_protect work, insofar that it's possible to add a custom upload handler that way.

However, something worth noting is that if you do this, your upload handlers will be receiving file data _before_ the CSRF checks are done. It might be worth mentioning this also in Django documentation - upload handlers receive data which hasn't been checked for CSRF yet.

comment:3 Changed 5 years ago by lukeplant

  • Resolution set to fixed
  • Status changed from assigned to closed

(In [13960]) Fixed #14182 - documented how to modify upload handlers when using CsrfViewMiddleware

Thanks to dc for the report.

comment:4 Changed 5 years ago by lukeplant

(In [13961]) [1.2.X] Fixed #14182 - documented how to modify upload handlers when using CsrfViewMiddleware

Thanks to dc for the report.

Backport of [13960] from trunk

Note: See TracTickets for help on using tickets.
Back to Top