mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	[1.10.x] Fixed CVE-2016-9014 -- Validated Host header when DEBUG=True.
This is a security fix.
This commit is contained in:
		| @@ -96,12 +96,13 @@ class HttpRequest(object): | |||||||
|         """Return the HTTP host using the environment or request headers.""" |         """Return the HTTP host using the environment or request headers.""" | ||||||
|         host = self._get_raw_host() |         host = self._get_raw_host() | ||||||
|  |  | ||||||
|         # There is no hostname validation when DEBUG=True |         # Allow variants of localhost if ALLOWED_HOSTS is empty and DEBUG=True. | ||||||
|         if settings.DEBUG: |         allowed_hosts = settings.ALLOWED_HOSTS | ||||||
|             return host |         if settings.DEBUG and not allowed_hosts: | ||||||
|  |             allowed_hosts = ['localhost', '127.0.0.1', '[::1]'] | ||||||
|  |  | ||||||
|         domain, port = split_domain_port(host) |         domain, port = split_domain_port(host) | ||||||
|         if domain and validate_host(domain, settings.ALLOWED_HOSTS): |         if domain and validate_host(domain, allowed_hosts): | ||||||
|             return host |             return host | ||||||
|         else: |         else: | ||||||
|             msg = "Invalid HTTP_HOST header: %r." % host |             msg = "Invalid HTTP_HOST header: %r." % host | ||||||
|   | |||||||
| @@ -90,14 +90,19 @@ If the ``Host`` header (or ``X-Forwarded-Host`` if | |||||||
| list, the :meth:`django.http.HttpRequest.get_host()` method will raise | list, the :meth:`django.http.HttpRequest.get_host()` method will raise | ||||||
| :exc:`~django.core.exceptions.SuspiciousOperation`. | :exc:`~django.core.exceptions.SuspiciousOperation`. | ||||||
|  |  | ||||||
| When :setting:`DEBUG` is ``True`` or when running tests, host validation is | When :setting:`DEBUG` is ``True`` and ``ALLOWED_HOSTS`` is empty, the host | ||||||
| disabled; any host will be accepted. Thus it's usually only necessary to set it | is validated against ``['localhost', '127.0.0.1', '[::1]']``. | ||||||
| in production. |  | ||||||
|  |  | ||||||
| This validation only applies via :meth:`~django.http.HttpRequest.get_host()`; | This validation only applies via :meth:`~django.http.HttpRequest.get_host()`; | ||||||
| if your code accesses the ``Host`` header directly from ``request.META`` you | if your code accesses the ``Host`` header directly from ``request.META`` you | ||||||
| are bypassing this security protection. | are bypassing this security protection. | ||||||
|  |  | ||||||
|  | .. versionchanged:: 1.10.3 | ||||||
|  |  | ||||||
|  |     In older versions, ``ALLOWED_HOSTS`` wasn't checked if ``DEBUG=True``. | ||||||
|  |     This was also changed in Django 1.9.11 and 1.8.16 to prevent a | ||||||
|  |     DNS rebinding attack. | ||||||
|  |  | ||||||
| .. setting:: APPEND_SLASH | .. setting:: APPEND_SLASH | ||||||
|  |  | ||||||
| ``APPEND_SLASH`` | ``APPEND_SLASH`` | ||||||
|   | |||||||
| @@ -20,6 +20,28 @@ the ``manage.py test --keepdb`` option or if the user has an active session | |||||||
|  |  | ||||||
| A randomly generated password is now used for each test run. | A randomly generated password is now used for each test run. | ||||||
|  |  | ||||||
|  | DNS rebinding vulnerability when ``DEBUG=True`` | ||||||
|  | =============================================== | ||||||
|  |  | ||||||
|  | Older versions of Django don't validate the ``Host`` header against | ||||||
|  | ``settings.ALLOWED_HOSTS`` when ``settings.DEBUG=True``. This makes them | ||||||
|  | vulnerable to a `DNS rebinding attack | ||||||
|  | <http://benmmurphy.github.io/blog/2016/07/11/rails-webconsole-dns-rebinding/>`_. | ||||||
|  |  | ||||||
|  | While Django doesn't ship a module that allows remote code execution, this is | ||||||
|  | at least a cross-site scripting vector, which could be quite serious if | ||||||
|  | developers load a copy of the production database in development or connect to | ||||||
|  | some production services for which there's no development instance, for | ||||||
|  | example. If a project uses a package like the ``django-debug-toolbar``, then | ||||||
|  | the attacker could execute arbitrary SQL, which could be especially bad if the | ||||||
|  | developers connect to the database with a superuser account. | ||||||
|  |  | ||||||
|  | ``settings.ALLOWED_HOSTS`` is now validated regardless of ``DEBUG``. For | ||||||
|  | convenience, if ``ALLOWED_HOSTS`` is empty and ``DEBUG=True``, the following | ||||||
|  | variations of localhost are allowed ``['localhost', '127.0.0.1', '::1']``. If | ||||||
|  | your local settings file has your production ``ALLOWED_HOSTS`` value, you must | ||||||
|  | now omit it to get those fallback values. | ||||||
|  |  | ||||||
| Bugfixes | Bugfixes | ||||||
| ======== | ======== | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,3 +19,25 @@ the ``manage.py test --keepdb`` option or if the user has an active session | |||||||
| (such as an attacker's connection). | (such as an attacker's connection). | ||||||
|  |  | ||||||
| A randomly generated password is now used for each test run. | A randomly generated password is now used for each test run. | ||||||
|  |  | ||||||
|  | DNS rebinding vulnerability when ``DEBUG=True`` | ||||||
|  | =============================================== | ||||||
|  |  | ||||||
|  | Older versions of Django don't validate the ``Host`` header against | ||||||
|  | ``settings.ALLOWED_HOSTS`` when ``settings.DEBUG=True``. This makes them | ||||||
|  | vulnerable to a `DNS rebinding attack | ||||||
|  | <http://benmmurphy.github.io/blog/2016/07/11/rails-webconsole-dns-rebinding/>`_. | ||||||
|  |  | ||||||
|  | While Django doesn't ship a module that allows remote code execution, this is | ||||||
|  | at least a cross-site scripting vector, which could be quite serious if | ||||||
|  | developers load a copy of the production database in development or connect to | ||||||
|  | some production services for which there's no development instance, for | ||||||
|  | example. If a project uses a package like the ``django-debug-toolbar``, then | ||||||
|  | the attacker could execute arbitrary SQL, which could be especially bad if the | ||||||
|  | developers connect to the database with a superuser account. | ||||||
|  |  | ||||||
|  | ``settings.ALLOWED_HOSTS`` is now validated regardless of ``DEBUG``. For | ||||||
|  | convenience, if ``ALLOWED_HOSTS`` is empty and ``DEBUG=True``, the following | ||||||
|  | variations of localhost are allowed ``['localhost', '127.0.0.1', '::1']``. If | ||||||
|  | your local settings file has your production ``ALLOWED_HOSTS`` value, you must | ||||||
|  | now omit it to get those fallback values. | ||||||
|   | |||||||
| @@ -19,3 +19,25 @@ the ``manage.py test --keepdb`` option or if the user has an active session | |||||||
| (such as an attacker's connection). | (such as an attacker's connection). | ||||||
|  |  | ||||||
| A randomly generated password is now used for each test run. | A randomly generated password is now used for each test run. | ||||||
|  |  | ||||||
|  | DNS rebinding vulnerability when ``DEBUG=True`` | ||||||
|  | =============================================== | ||||||
|  |  | ||||||
|  | Older versions of Django don't validate the ``Host`` header against | ||||||
|  | ``settings.ALLOWED_HOSTS`` when ``settings.DEBUG=True``. This makes them | ||||||
|  | vulnerable to a `DNS rebinding attack | ||||||
|  | <http://benmmurphy.github.io/blog/2016/07/11/rails-webconsole-dns-rebinding/>`_. | ||||||
|  |  | ||||||
|  | While Django doesn't ship a module that allows remote code execution, this is | ||||||
|  | at least a cross-site scripting vector, which could be quite serious if | ||||||
|  | developers load a copy of the production database in development or connect to | ||||||
|  | some production services for which there's no development instance, for | ||||||
|  | example. If a project uses a package like the ``django-debug-toolbar``, then | ||||||
|  | the attacker could execute arbitrary SQL, which could be especially bad if the | ||||||
|  | developers connect to the database with a superuser account. | ||||||
|  |  | ||||||
|  | ``settings.ALLOWED_HOSTS`` is now validated regardless of ``DEBUG``. For | ||||||
|  | convenience, if ``ALLOWED_HOSTS`` is empty and ``DEBUG=True``, the following | ||||||
|  | variations of localhost are allowed ``['localhost', '127.0.0.1', '::1']``. If | ||||||
|  | your local settings file has your production ``ALLOWED_HOSTS`` value, you must | ||||||
|  | now omit it to get those fallback values. | ||||||
|   | |||||||
| @@ -377,7 +377,7 @@ class CsrfViewMiddlewareTest(SimpleTestCase): | |||||||
|         self.assertEqual(len(csrf_cookie.value), CSRF_TOKEN_LENGTH) |         self.assertEqual(len(csrf_cookie.value), CSRF_TOKEN_LENGTH) | ||||||
|         self._check_token_present(resp, csrf_id=csrf_cookie.value) |         self._check_token_present(resp, csrf_id=csrf_cookie.value) | ||||||
|  |  | ||||||
|     @override_settings(DEBUG=True) |     @override_settings(DEBUG=True, ALLOWED_HOSTS=['www.example.com']) | ||||||
|     def test_https_bad_referer(self): |     def test_https_bad_referer(self): | ||||||
|         """ |         """ | ||||||
|         Test that a POST HTTPS request with a bad referer is rejected |         Test that a POST HTTPS request with a bad referer is rejected | ||||||
|   | |||||||
| @@ -756,21 +756,22 @@ class HostValidationTests(SimpleTestCase): | |||||||
|         self.assertEqual(request.get_port(), '8080') |         self.assertEqual(request.get_port(), '8080') | ||||||
|  |  | ||||||
|     @override_settings(DEBUG=True, ALLOWED_HOSTS=[]) |     @override_settings(DEBUG=True, ALLOWED_HOSTS=[]) | ||||||
|     def test_host_validation_disabled_in_debug_mode(self): |     def test_host_validation_in_debug_mode(self): | ||||||
|         """If ALLOWED_HOSTS is empty and DEBUG is True, all hosts pass.""" |         """ | ||||||
|         request = HttpRequest() |         If ALLOWED_HOSTS is empty and DEBUG is True, variants of localhost are | ||||||
|         request.META = { |         allowed. | ||||||
|             'HTTP_HOST': 'example.com', |         """ | ||||||
|         } |         valid_hosts = ['localhost', '127.0.0.1', '[::1]'] | ||||||
|         self.assertEqual(request.get_host(), 'example.com') |         for host in valid_hosts: | ||||||
|  |             request = HttpRequest() | ||||||
|  |             request.META = {'HTTP_HOST': host} | ||||||
|  |             self.assertEqual(request.get_host(), host) | ||||||
|  |  | ||||||
|         # Invalid hostnames would normally raise a SuspiciousOperation, |         # Other hostnames raise a SuspiciousOperation. | ||||||
|         # but we have DEBUG=True, so this check is disabled. |         with self.assertRaises(SuspiciousOperation): | ||||||
|         request = HttpRequest() |             request = HttpRequest() | ||||||
|         request.META = { |             request.META = {'HTTP_HOST': 'example.com'} | ||||||
|             'HTTP_HOST': "invalid_hostname.com", |             request.get_host() | ||||||
|         } |  | ||||||
|         self.assertEqual(request.get_host(), "invalid_hostname.com") |  | ||||||
|  |  | ||||||
|     @override_settings(ALLOWED_HOSTS=[]) |     @override_settings(ALLOWED_HOSTS=[]) | ||||||
|     def test_get_host_suggestion_of_allowed_host(self): |     def test_get_host_suggestion_of_allowed_host(self): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user