mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
[3.2.x] Fixed CVE-2022-36359 -- Escaped filename in Content-Disposition header.
Thanks to Motoyasu Saburi for the report.
This commit is contained in:
parent
cb7fbac9f8
commit
b3e4494d75
@ -485,7 +485,9 @@ class FileResponse(StreamingHttpResponse):
|
|||||||
disposition = 'attachment' if self.as_attachment else 'inline'
|
disposition = 'attachment' if self.as_attachment else 'inline'
|
||||||
try:
|
try:
|
||||||
filename.encode('ascii')
|
filename.encode('ascii')
|
||||||
file_expr = 'filename="{}"'.format(filename)
|
file_expr = 'filename="{}"'.format(
|
||||||
|
filename.replace('\\', '\\\\').replace('"', r'\"')
|
||||||
|
)
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
file_expr = "filename*=utf-8''{}".format(quote(filename))
|
file_expr = "filename*=utf-8''{}".format(quote(filename))
|
||||||
self.headers['Content-Disposition'] = '{}; {}'.format(disposition, file_expr)
|
self.headers['Content-Disposition'] = '{}; {}'.format(disposition, file_expr)
|
||||||
|
@ -6,4 +6,10 @@ Django 3.2.15 release notes
|
|||||||
|
|
||||||
Django 3.2.15 fixes a security issue with severity "high" in 3.2.14.
|
Django 3.2.15 fixes a security issue with severity "high" in 3.2.14.
|
||||||
|
|
||||||
...
|
CVE-2022-36359: Potential reflected file download vulnerability in ``FileResponse``
|
||||||
|
===================================================================================
|
||||||
|
|
||||||
|
An application may have been vulnerable to a reflected file download (RFD)
|
||||||
|
attack that sets the Content-Disposition header of a
|
||||||
|
:class:`~django.http.FileResponse` when the ``filename`` was derived from
|
||||||
|
user-supplied input. The ``filename`` is now escaped to avoid this possibility.
|
||||||
|
@ -89,3 +89,38 @@ class FileResponseTests(SimpleTestCase):
|
|||||||
response.headers['Content-Disposition'],
|
response.headers['Content-Disposition'],
|
||||||
"attachment; filename*=utf-8''%E7%A5%9D%E6%82%A8%E5%B9%B3%E5%AE%89.odt"
|
"attachment; filename*=utf-8''%E7%A5%9D%E6%82%A8%E5%B9%B3%E5%AE%89.odt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_content_disposition_escaping(self):
|
||||||
|
# fmt: off
|
||||||
|
tests = [
|
||||||
|
(
|
||||||
|
'multi-part-one";\" dummy".txt',
|
||||||
|
r"multi-part-one\";\" dummy\".txt"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
# fmt: on
|
||||||
|
# Non-escape sequence backslashes are path segments on Windows, and are
|
||||||
|
# eliminated by an os.path.basename() check in FileResponse.
|
||||||
|
if sys.platform != "win32":
|
||||||
|
# fmt: off
|
||||||
|
tests += [
|
||||||
|
(
|
||||||
|
'multi-part-one\\";\" dummy".txt',
|
||||||
|
r"multi-part-one\\\";\" dummy\".txt"
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'multi-part-one\\";\\\" dummy".txt',
|
||||||
|
r"multi-part-one\\\";\\\" dummy\".txt"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
# fmt: on
|
||||||
|
for filename, escaped in tests:
|
||||||
|
with self.subTest(filename=filename, escaped=escaped):
|
||||||
|
response = FileResponse(
|
||||||
|
io.BytesIO(b"binary content"), filename=filename, as_attachment=True
|
||||||
|
)
|
||||||
|
response.close()
|
||||||
|
self.assertEqual(
|
||||||
|
response.headers["Content-Disposition"],
|
||||||
|
f'attachment; filename="{escaped}"',
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user