#28132 closed Bug (fixed)
File upload crash with "TemporaryFileUploadHandler object has no attribute 'file'" error
Reported by: | Michal Čihař | Owned by: | Michael Brown |
---|---|---|---|
Component: | File uploads/storage | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Michael Brown | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Unfortunately I don't have a reproducer and I'm still on 1.10 on production, but I still think it's worth reporting.
On file upload following error is raised:
Traceback: File "/usr/lib/python2.7/dist-packages/django/core/handlers/exception.py" in inner 42. response = get_response(request) File "/usr/lib/python2.7/dist-packages/django/core/handlers/base.py" in _legacy_get_response 249. response = self._get_response(request) File "/usr/lib/python2.7/dist-packages/django/core/handlers/base.py" in _get_response 178. response = middleware_method(request, callback, callback_args, callback_kwargs) File "/usr/lib/python2.7/dist-packages/django/middleware/csrf.py" in process_view 260. request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') File "/usr/lib/python2.7/dist-packages/django/core/handlers/wsgi.py" in _get_post 128. self._load_post_and_files() File "/usr/lib/python2.7/dist-packages/django/http/request.py" in _load_post_and_files 299. self._post, self._files = self.parse_file_upload(self.META, data) File "/usr/lib/python2.7/dist-packages/django/http/request.py" in parse_file_upload 258. return parser.parse() File "/usr/lib/python2.7/dist-packages/django/http/multipartparser.py" in parse 163. self.handle_file_complete(old_field_name, counters) File "/usr/lib/python2.7/dist-packages/django/http/multipartparser.py" in handle_file_complete 301. file_obj = handler.file_complete(counters[i]) File "/usr/lib/python2.7/dist-packages/django/core/files/uploadhandler.py" in file_complete 153. self.file.seek(0) Exception Type: AttributeError at /dictionaries/kallithea/be/upload/ Exception Value: 'TemporaryFileUploadHandler' object has no attribute 'file'
Change History (12)
comment:1 by , 8 years ago
comment:2 by , 8 years ago
Description: | modified (diff) |
---|
comment:3 by , 8 years ago
Sorry, just added that.
It seems that TemporaryFileUploadHandler.new_file was never called, but TemporaryFileUploadHandler.file_complete was.
And the upload handlers settings is unchanged, so using defaults:
FILE_UPLOAD_HANDLERS = [ 'django.core.files.uploadhandler.MemoryFileUploadHandler', 'django.core.files.uploadhandler.TemporaryFileUploadHandler', ]
comment:4 by , 8 years ago
Also I should probably mention that this was discovered once our project was publicly exposed at HackerOne, so it's possible that the payload has been tampered to trigger this.
comment:5 by , 8 years ago
Component: | Uncategorized → File uploads/storage |
---|---|
Resolution: | → needsinfo |
Status: | new → closed |
Summary: | Error on file upload → File upload crash with "TemporaryFileUploadHandler object has no attribute 'file'" error |
Closing as "needsinfo" until we have steps to reproduce.
comment:6 by , 7 years ago
I've recently seen the same bug (Django 1.11). It was also during a security review. I managed to save some POST arguments and I guess the problem is caused by the completely invalid file
and name
arguments, namely:
"file": "'", "name": "'",
I'll try to create a test case tomorrow if I have time.
comment:7 by , 4 years ago
Cc: | added |
---|---|
Has patch: | set |
Resolution: | needsinfo |
Status: | closed → new |
Version: | 1.10 → master |
I figured out how to reproduce this bug. It occurs whenever an uploaded filename ends in "/".
I have a pull request open here: https://github.com/django/django/pull/13035
This is caused by a blank filename after it is sanitized. Although MultiPartParser.parse() sanitizes and checks for empty filenames before handling the uploaded file, the filename can later be blank after it is sanitized in django.core.files.uploadedfile.UploadedFile._set_name.
For UploadedFile objects, the truthiness is False when filename is blank, so the MemoryFileUploadHandler returns an object that evaluates to False. In MultiPartParser.handle_file_complete, the return value from handler.file_complete() is checked for truthiness. That handler is erroneously skipped because of the blank filename and the next handler is called, even though the next handler never had new_file() called on it.
My patch adds another sanitize step for the filename in MultiPartParser.parse(), which is the same step performed in UploadedFile:
file_name = os.path.basename(file_name)
It also checks for None instead of truthiness to determine when handlers are skipped in MultiPartParser.handle_file_complete():
file_obj = handler.file_complete(counters[i]) - if file_obj: + if file_obj is not None:
I don't think any documentation updates are needed, since the docs for FileUploadHandler.file_complete() say "Handlers may also return None to indicate that the UploadedFile object should come from subsequent upload handlers." I don't know if this could be a backward compatibility problem for 3rd-party code though.
comment:8 by , 4 years ago
I worked with Michael to help track this down. In the process, I came up with this curl command that reproduces the bug. Since the multipart request parsing happens so early in the request processing, I believe this will trigger a 500 error on any django view on any deployment that uses the default set of file upload handlers.
curl http://localhost:8080/ -H "Content-Type: multipart/form-data; boundary=BoUnDaRy" --data-binary $'Content-Disposition: form-data; name="foo"; filename="foo/"\r\n\r\nfoo\r\n--BoUnDaRy\r\n' -b csrftoken=foo
(change the url to point at your local django dev server)
Once patched, this command should error out at the lack of a csrf token in the form data.
comment:9 by , 4 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
You only included the traceback, not the error itself. Could you add the error?