Ensured views.static.serve() doesn't use large memory on large files.

This issue was fixed in master by refs #24072.
This commit is contained in:
Tim Graham 2014-12-09 15:32:03 -05:00
parent 69b5e66738
commit a3bebfdc34
5 changed files with 55 additions and 1 deletions

View File

@ -45,6 +45,21 @@ from a XSS attack. This bug doesn't affect Django currently, since we only put
this URL into the ``Location`` response header and browsers seem to ignore this URL into the ``Location`` response header and browsers seem to ignore
JavaScript there. JavaScript there.
Denial-of-service attack against ``django.views.static.serve``
==============================================================
In older versions of Django, the :func:`django.views.static.serve` view read
the files it served one line at a time. Therefore, a big file with no newlines
would result in memory usage equal to the size of that file. An attacker could
exploit this and launch a denial-of-service attack by simultaneously requesting
many large files. This view now reads the file in chunks to prevent large
memory usage.
Note, however, that this view has always carried a warning that it is not
hardened for production use and should be used only as a development aid. Now
may be a good time to audit your project and serve your files in production
using a real front-end web server if you are not doing so.
Bugfixes Bugfixes
======== ========

View File

@ -43,3 +43,18 @@ provide safe redirect targets and put such a URL into a link, they could suffer
from a XSS attack. This bug doesn't affect Django currently, since we only put from a XSS attack. This bug doesn't affect Django currently, since we only put
this URL into the ``Location`` response header and browsers seem to ignore this URL into the ``Location`` response header and browsers seem to ignore
JavaScript there. JavaScript there.
Denial-of-service attack against ``django.views.static.serve``
==============================================================
In older versions of Django, the :func:`django.views.static.serve` view read
the files it served one line at a time. Therefore, a big file with no newlines
would result in memory usage equal to the size of that file. An attacker could
exploit this and launch a denial-of-service attack by simultaneously requesting
many large files. This view now reads the file in chunks to prevent large
memory usage.
Note, however, that this view has always carried a warning that it is not
hardened for production use and should be used only as a development aid. Now
may be a good time to audit your project and serve your files in production
using a real front-end web server if you are not doing so.

View File

@ -44,6 +44,21 @@ from a XSS attack. This bug doesn't affect Django currently, since we only put
this URL into the ``Location`` response header and browsers seem to ignore this URL into the ``Location`` response header and browsers seem to ignore
JavaScript there. JavaScript there.
Denial-of-service attack against ``django.views.static.serve``
==============================================================
In older versions of Django, the :func:`django.views.static.serve` view read
the files it served one line at a time. Therefore, a big file with no newlines
would result in memory usage equal to the size of that file. An attacker could
exploit this and launch a denial-of-service attack by simultaneously requesting
many large files. This view now reads the file in chunks to prevent large
memory usage.
Note, however, that this view has always carried a warning that it is not
hardened for production use and should be used only as a development aid. Now
may be a good time to audit your project and serve your files in production
using a real front-end web server if you are not doing so.
Bugfixes Bugfixes
======== ========

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@ from os import path
import unittest import unittest
from django.conf.urls.static import static from django.conf.urls.static import static
from django.http import HttpResponseNotModified from django.http import FileResponse, HttpResponseNotModified
from django.test import SimpleTestCase, override_settings from django.test import SimpleTestCase, override_settings
from django.utils.http import http_date from django.utils.http import http_date
from django.views.static import was_modified_since from django.views.static import was_modified_since
@ -32,6 +32,14 @@ class StaticTests(SimpleTestCase):
self.assertEqual(len(response_content), int(response['Content-Length'])) self.assertEqual(len(response_content), int(response['Content-Length']))
self.assertEqual(mimetypes.guess_type(file_path)[1], response.get('Content-Encoding', None)) self.assertEqual(mimetypes.guess_type(file_path)[1], response.get('Content-Encoding', None))
def test_chunked(self):
"The static view should stream files in chunks to avoid large memory usage"
response = self.client.get('/%s/%s' % (self.prefix, 'long-line.txt'))
first_chunk = next(response.streaming_content)
self.assertEqual(len(first_chunk), FileResponse.block_size)
second_chunk = next(response.streaming_content)
self.assertEqual(len(second_chunk), 1451)
def test_unknown_mime_type(self): def test_unknown_mime_type(self):
response = self.client.get('/%s/file.unknown' % self.prefix) response = self.client.get('/%s/file.unknown' % self.prefix)
self.assertEqual('application/octet-stream', response['Content-Type']) self.assertEqual('application/octet-stream', response['Content-Type'])