mirror of
https://github.com/django/django.git
synced 2025-10-26 23:26:08 +00:00
Fixed #20038 -- Better error message for host validation.
This commit is contained in:
committed by
Carl Meyer
parent
c8deaa9e7b
commit
c250f9c99b
@@ -4,7 +4,6 @@ import copy
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from io import BytesIO
|
||||
from pprint import pformat
|
||||
try:
|
||||
@@ -66,11 +65,14 @@ class HttpRequest(object):
|
||||
host = '%s:%s' % (host, server_port)
|
||||
|
||||
allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS
|
||||
if validate_host(host, allowed_hosts):
|
||||
domain, port = split_domain_port(host)
|
||||
if domain and validate_host(domain, allowed_hosts):
|
||||
return host
|
||||
else:
|
||||
raise SuspiciousOperation(
|
||||
"Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)
|
||||
msg = "Invalid HTTP_HOST header: %r." % host
|
||||
if domain:
|
||||
msg += "You may need to add %r to ALLOWED_HOSTS." % domain
|
||||
raise SuspiciousOperation(msg)
|
||||
|
||||
def get_full_path(self):
|
||||
# RFC 3986 requires query string arguments to be in the ASCII range.
|
||||
@@ -454,9 +456,30 @@ def bytes_to_text(s, encoding):
|
||||
return s
|
||||
|
||||
|
||||
def split_domain_port(host):
|
||||
"""
|
||||
Return a (domain, port) tuple from a given host.
|
||||
|
||||
Returned domain is lower-cased. If the host is invalid, the domain will be
|
||||
empty.
|
||||
"""
|
||||
host = host.lower()
|
||||
|
||||
if not host_validation_re.match(host):
|
||||
return '', ''
|
||||
|
||||
if host[-1] == ']':
|
||||
# It's an IPv6 address without a port.
|
||||
return host, ''
|
||||
bits = host.rsplit(':', 1)
|
||||
if len(bits) == 2:
|
||||
return tuple(bits)
|
||||
return bits[0], ''
|
||||
|
||||
|
||||
def validate_host(host, allowed_hosts):
|
||||
"""
|
||||
Validate the given host header value for this site.
|
||||
Validate the given host for this site.
|
||||
|
||||
Check that the host looks valid and matches a host or host pattern in the
|
||||
given list of ``allowed_hosts``. Any pattern beginning with a period
|
||||
@@ -464,31 +487,20 @@ def validate_host(host, allowed_hosts):
|
||||
``example.com`` and any subdomain), ``*`` matches anything, and anything
|
||||
else must match exactly.
|
||||
|
||||
Note: This function assumes that the given host is lower-cased and has
|
||||
already had the port, if any, stripped off.
|
||||
|
||||
Return ``True`` for a valid host, ``False`` otherwise.
|
||||
|
||||
"""
|
||||
# All validation is case-insensitive
|
||||
host = host.lower()
|
||||
|
||||
# Basic sanity check
|
||||
if not host_validation_re.match(host):
|
||||
return False
|
||||
|
||||
# Validate only the domain part.
|
||||
if host[-1] == ']':
|
||||
# It's an IPv6 address without a port.
|
||||
domain = host
|
||||
else:
|
||||
domain = host.rsplit(':', 1)[0]
|
||||
|
||||
for pattern in allowed_hosts:
|
||||
pattern = pattern.lower()
|
||||
match = (
|
||||
pattern == '*' or
|
||||
pattern.startswith('.') and (
|
||||
domain.endswith(pattern) or domain == pattern[1:]
|
||||
host.endswith(pattern) or host == pattern[1:]
|
||||
) or
|
||||
pattern == domain
|
||||
pattern == host
|
||||
)
|
||||
if match:
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user