File tree Expand file tree Collapse file tree 4 files changed +52
-3
lines changed
Expand file tree Collapse file tree 4 files changed +52
-3
lines changed Original file line number Diff line number Diff line change @@ -520,7 +520,9 @@ def set_headers(self, filelike):
520520 disposition = "attachment" if self .as_attachment else "inline"
521521 try :
522522 filename .encode ("ascii" )
523- file_expr = 'filename="{}"' .format (filename )
523+ file_expr = 'filename="{}"' .format (
524+ filename .replace ("\\ " , "\\ \\ " ).replace ('"' , r"\"" )
525+ )
524526 except UnicodeEncodeError :
525527 file_expr = "filename*=utf-8''{}" .format (quote (filename ))
526528 self .headers ["Content-Disposition" ] = "{}; {}" .format (
Original file line number Diff line number Diff line change @@ -6,4 +6,10 @@ Django 3.2.15 release notes
66
77Django 3.2.15 fixes a security issue with severity "high" in 3.2.14.
88
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 @@ -6,4 +6,10 @@ Django 4.0.7 release notes
66
77Django 4.0.7 fixes a security issue with severity "high" in 4.0.6.
88
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 @@ -101,3 +101,38 @@ def test_repr(self):
101101 repr (response ),
102102 '<FileResponse status_code=200, "application/octet-stream">' ,
103103 )
104+
105+ def test_content_disposition_escaping (self ):
106+ # fmt: off
107+ tests = [
108+ (
109+ 'multi-part-one";\" dummy".txt' ,
110+ r"multi-part-one\";\" dummy\".txt"
111+ ),
112+ ]
113+ # fmt: on
114+ # Non-escape sequence backslashes are path segments on Windows, and are
115+ # eliminated by an os.path.basename() check in FileResponse.
116+ if sys .platform != "win32" :
117+ # fmt: off
118+ tests += [
119+ (
120+ 'multi-part-one\\ ";\" dummy".txt' ,
121+ r"multi-part-one\\\";\" dummy\".txt"
122+ ),
123+ (
124+ 'multi-part-one\\ ";\\ \" dummy".txt' ,
125+ r"multi-part-one\\\";\\\" dummy\".txt"
126+ )
127+ ]
128+ # fmt: on
129+ for filename , escaped in tests :
130+ with self .subTest (filename = filename , escaped = escaped ):
131+ response = FileResponse (
132+ io .BytesIO (b"binary content" ), filename = filename , as_attachment = True
133+ )
134+ response .close ()
135+ self .assertEqual (
136+ response .headers ["Content-Disposition" ],
137+ f'attachment; filename="{ escaped } "' ,
138+ )
You can’t perform that action at this time.
0 commit comments