+45
-2
lines changedFilter options
+45
-2
lines changed Original file line number Diff line number Diff line change
@@ -485,7 +485,9 @@ def set_headers(self, filelike):
485
485
disposition = 'attachment' if self.as_attachment else 'inline'
486
486
try:
487
487
filename.encode('ascii')
488
-
file_expr = 'filename="{}"'.format(filename)
488
+
file_expr = 'filename="{}"'.format(
489
+
filename.replace('\\', '\\\\').replace('"', r'\"')
490
+
)
489
491
except UnicodeEncodeError:
490
492
file_expr = "filename*=utf-8''{}".format(quote(filename))
491
493
self.headers['Content-Disposition'] = '{}; {}'.format(disposition, file_expr)
Original file line number Diff line number Diff line change
@@ -6,4 +6,10 @@ Django 3.2.15 release notes
6
6
7
7
Django 3.2.15 fixes a security issue with severity "high" in 3.2.14.
8
8
9
-
...
9
+
CVE-2022-36359: Potential reflected file download vulnerability in ``FileResponse``
10
+
===================================================================================
11
+
12
+
An application may have been vulnerable to a reflected file download (RFD)
13
+
attack that sets the Content-Disposition header of a
14
+
:class:`~django.http.FileResponse` when the ``filename`` was derived from
15
+
user-supplied input. The ``filename`` is now escaped to avoid this possibility.
Original file line number Diff line number Diff line change
@@ -89,3 +89,38 @@ def test_unicode_attachment(self):
89
89
response.headers['Content-Disposition'],
90
90
"attachment; filename*=utf-8''%E7%A5%9D%E6%82%A8%E5%B9%B3%E5%AE%89.odt"
91
91
)
92
+
93
+
def test_content_disposition_escaping(self):
94
+
# fmt: off
95
+
tests = [
96
+
(
97
+
'multi-part-one";\" dummy".txt',
98
+
r"multi-part-one\";\" dummy\".txt"
99
+
),
100
+
]
101
+
# fmt: on
102
+
# Non-escape sequence backslashes are path segments on Windows, and are
103
+
# eliminated by an os.path.basename() check in FileResponse.
104
+
if sys.platform != "win32":
105
+
# fmt: off
106
+
tests += [
107
+
(
108
+
'multi-part-one\\";\" dummy".txt',
109
+
r"multi-part-one\\\";\" dummy\".txt"
110
+
),
111
+
(
112
+
'multi-part-one\\";\\\" dummy".txt',
113
+
r"multi-part-one\\\";\\\" dummy\".txt"
114
+
)
115
+
]
116
+
# fmt: on
117
+
for filename, escaped in tests:
118
+
with self.subTest(filename=filename, escaped=escaped):
119
+
response = FileResponse(
120
+
io.BytesIO(b"binary content"), filename=filename, as_attachment=True
121
+
)
122
+
response.close()
123
+
self.assertEqual(
124
+
response.headers["Content-Disposition"],
125
+
f'attachment; filename="{escaped}"',
126
+
)
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4