summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Hacohen <tasn@users.noreply.github.com>2019-01-04 02:21:55 +0000
committerTim Graham <timograham@gmail.com>2019-01-03 21:22:14 -0500
commit64d2396e83aedba3fcc84ca40f23fbd22f0b9b5b (patch)
tree09d4128c86d14d24f81a1989d81c17ba1edf0071
parente45d466d37af4b58c0a3201d31335db2883e2983 (diff)
[2.1.x] Fixed #30070, CVE-2019-3498 -- Fixed content spoofing possiblity in the default 404 page.
Co-Authored-By: Tim Graham <timograham@gmail.com> Backport of 1ecc0a395be721e987e8e9fdfadde952b6dee1c7 from master.
-rw-r--r--django/views/defaults.py9
-rw-r--r--docs/releases/1.11.18.txt18
-rw-r--r--docs/releases/2.0.10.txt15
-rw-r--r--docs/releases/2.1.5.txt14
-rw-r--r--docs/releases/index.txt1
-rw-r--r--tests/handlers/tests.py10
6 files changed, 56 insertions, 11 deletions
diff --git a/django/views/defaults.py b/django/views/defaults.py
index dee081ec3d..6c394490ab 100644
--- a/django/views/defaults.py
+++ b/django/views/defaults.py
@@ -1,3 +1,5 @@
+from urllib.parse import quote
+
from django.http import (
HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound,
HttpResponseServerError,
@@ -22,7 +24,8 @@ def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME):
Templates: :template:`404.html`
Context:
request_path
- The path of the requested URL (e.g., '/app/pages/bad_page/')
+ The path of the requested URL (e.g., '/app/pages/bad_page/'). It's
+ quoted to prevent a content injection attack.
exception
The message from the exception which triggered the 404 (if one was
supplied), or the exception class name
@@ -38,7 +41,7 @@ def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME):
if isinstance(message, str):
exception_repr = message
context = {
- 'request_path': request.path,
+ 'request_path': quote(request.path),
'exception': exception_repr,
}
try:
@@ -51,7 +54,7 @@ def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME):
raise
template = Engine().from_string(
'<h1>Not Found</h1>'
- '<p>The requested URL {{ request_path }} was not found on this server.</p>')
+ '<p>The requested resource was not found on this server.</p>')
body = template.render(Context(context))
content_type = 'text/html'
return HttpResponseNotFound(body, content_type=content_type)
diff --git a/docs/releases/1.11.18.txt b/docs/releases/1.11.18.txt
new file mode 100644
index 0000000000..82a229e6dd
--- /dev/null
+++ b/docs/releases/1.11.18.txt
@@ -0,0 +1,18 @@
+============================
+Django 1.11.18 release notes
+============================
+
+*January 4, 2019*
+
+Django 1.11.18 fixes a security issue in 1.11.17.
+
+CVE-2019-3498: Content spoofing possibility in the default 404 page
+-------------------------------------------------------------------
+
+An attacker could craft a malicious URL that could make spoofed content appear
+on the default page generated by the ``django.views.defaults.page_not_found()``
+view.
+
+The URL path is no longer displayed in the default 404 template and the
+``request_path`` context variable is now quoted to fix the issue for custom
+templates that use the path.
diff --git a/docs/releases/2.0.10.txt b/docs/releases/2.0.10.txt
index 18901490e0..8b0bf3a2a2 100644
--- a/docs/releases/2.0.10.txt
+++ b/docs/releases/2.0.10.txt
@@ -2,9 +2,20 @@
Django 2.0.10 release notes
===========================
-*Release date TBD*
+*January 4, 2019*
-Django 2.0.10 fixes several bugs in 2.0.9.
+Django 2.0.10 fixes a security issue and several bugs in 2.0.9.
+
+CVE-2019-3498: Content spoofing possibility in the default 404 page
+-------------------------------------------------------------------
+
+An attacker could craft a malicious URL that could make spoofed content appear
+on the default page generated by the ``django.views.defaults.page_not_found()``
+view.
+
+The URL path is no longer displayed in the default 404 template and the
+``request_path`` context variable is now quoted to fix the issue for custom
+templates that use the path.
Bugfixes
========
diff --git a/docs/releases/2.1.5.txt b/docs/releases/2.1.5.txt
index 27ffbc7510..ebe775a3d3 100644
--- a/docs/releases/2.1.5.txt
+++ b/docs/releases/2.1.5.txt
@@ -2,10 +2,20 @@
Django 2.1.5 release notes
==========================
-*Expected January 1, 2019*
+*January 4, 2019*
+Django 2.1.5 fixes a security issue and several bugs in 2.1.4.
-Django 2.1.5 fixes several bugs in 2.1.4.
+CVE-2019-3498: Content spoofing possibility in the default 404 page
+-------------------------------------------------------------------
+
+An attacker could craft a malicious URL that could make spoofed content appear
+on the default page generated by the ``django.views.defaults.page_not_found()``
+view.
+
+The URL path is no longer displayed in the default 404 template and the
+``request_path`` context variable is now quoted to fix the issue for custom
+templates that use the path.
Bugfixes
========
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index c58a7ef071..0550d92f91 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -54,6 +54,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 1.11.18
1.11.17
1.11.16
1.11.15
diff --git a/tests/handlers/tests.py b/tests/handlers/tests.py
index 175a892618..54596a4f72 100644
--- a/tests/handlers/tests.py
+++ b/tests/handlers/tests.py
@@ -5,6 +5,7 @@ from django.db import close_old_connections, connection
from django.test import (
RequestFactory, SimpleTestCase, TransactionTestCase, override_settings,
)
+from django.utils.version import PY37
class HandlerTests(SimpleTestCase):
@@ -160,16 +161,17 @@ class HandlerRequestTests(SimpleTestCase):
def test_invalid_urls(self):
response = self.client.get('~%A9helloworld')
- self.assertContains(response, '~%A9helloworld', status_code=404)
+ self.assertEqual(response.status_code, 404)
+ self.assertEqual(response.context['request_path'], '/~%25A9helloworld' if PY37 else '/%7E%25A9helloworld')
response = self.client.get('d%aao%aaw%aan%aal%aao%aaa%aad%aa/')
- self.assertContains(response, 'd%AAo%AAw%AAn%AAl%AAo%AAa%AAd%AA', status_code=404)
+ self.assertEqual(response.context['request_path'], '/d%25AAo%25AAw%25AAn%25AAl%25AAo%25AAa%25AAd%25AA')
response = self.client.get('/%E2%99%E2%99%A5/')
- self.assertContains(response, '%E2%99\u2665', status_code=404)
+ self.assertEqual(response.context['request_path'], '/%25E2%2599%E2%99%A5/')
response = self.client.get('/%E2%98%8E%E2%A9%E2%99%A5/')
- self.assertContains(response, '\u260e%E2%A9\u2665', status_code=404)
+ self.assertEqual(response.context['request_path'], '/%E2%98%8E%25E2%25A9%E2%99%A5/')
def test_environ_path_info_type(self):
environ = RequestFactory().get('/%E2%A8%87%87%A5%E2%A8%A0').environ