summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2022-07-17 16:36:25 -0400
committerGitHub <noreply@github.com>2022-07-17 16:36:25 -0400
commit5964c48b466448a14703eecc062223fda46caf4e (patch)
tree8589e0b16b01ed6f522ef5eb597b3202884d282d
parent12a06bf717effe21973953f30771a56c74130621 (diff)
parent72d94e3adf2786aaa723f3f1b5bf8a9b34f28332 (diff)
downloadcherrypy-git-5964c48b466448a14703eecc062223fda46caf4e.tar.gz
Merge pull request #1976 from cherrypy/bugfix/1974-dangerous-hostv18.8.0
Fix issues with dangerous host headers.
-rw-r--r--CHANGES.rst9
-rw-r--r--cherrypy/_cprequest.py3
-rw-r--r--cherrypy/lib/httputil.py30
-rw-r--r--cherrypy/test/test_request_obj.py10
4 files changed, 52 insertions, 0 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 697d77ef..35b3f895 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,12 @@
+v18.8.0
+-------
+
+* :issue:`1974`: Dangerous characters received in a host header
+ encoded using RFC 2047 are now elided by default. Currently,
+ dangerous characters are defined as CR and LF. The original
+ value is still available as ``cherrypy.request.headers['Host'].raw``
+ if needed.
+
v18.7.0
-------
diff --git a/cherrypy/_cprequest.py b/cherrypy/_cprequest.py
index b380bb75..a661112c 100644
--- a/cherrypy/_cprequest.py
+++ b/cherrypy/_cprequest.py
@@ -742,6 +742,9 @@ class Request(object):
if self.protocol >= (1, 1):
msg = "HTTP/1.1 requires a 'Host' request header."
raise cherrypy.HTTPError(400, msg)
+ else:
+ headers['Host'] = httputil.SanitizedHost(dict.get(headers, 'Host'))
+
host = dict.get(headers, 'Host')
if not host:
host = self.local.name or self.local.ip
diff --git a/cherrypy/lib/httputil.py b/cherrypy/lib/httputil.py
index eedf8d89..ced310a0 100644
--- a/cherrypy/lib/httputil.py
+++ b/cherrypy/lib/httputil.py
@@ -516,3 +516,33 @@ class Host(object):
def __repr__(self):
return 'httputil.Host(%r, %r, %r)' % (self.ip, self.port, self.name)
+
+
+class SanitizedHost(str):
+ r"""
+ Wraps a raw host header received from the network in
+ a sanitized version that elides dangerous characters.
+
+ >>> SanitizedHost('foo\nbar')
+ 'foobar'
+ >>> SanitizedHost('foo\nbar').raw
+ 'foo\nbar'
+
+ A SanitizedInstance is only returned if sanitization was performed.
+
+ >>> isinstance(SanitizedHost('foobar'), SanitizedHost)
+ False
+ """
+ dangerous = re.compile(r'[\n\r]')
+
+ def __new__(cls, raw):
+ sanitized = cls._sanitize(raw)
+ if sanitized == raw:
+ return raw
+ instance = super().__new__(cls, sanitized)
+ instance.raw = raw
+ return instance
+
+ @classmethod
+ def _sanitize(cls, raw):
+ return cls.dangerous.sub('', raw)
diff --git a/cherrypy/test/test_request_obj.py b/cherrypy/test/test_request_obj.py
index 3aaa8e81..2478aabe 100644
--- a/cherrypy/test/test_request_obj.py
+++ b/cherrypy/test/test_request_obj.py
@@ -756,6 +756,16 @@ class RequestObjectTests(helper.CPWebCase):
headers=[('Content-type', 'application/json')])
self.assertBody('application/json')
+ def test_dangerous_host(self):
+ """
+ Dangerous characters like newlines should be elided.
+ Ref #1974.
+ """
+ # foo\nbar
+ encoded = '=?iso-8859-1?q?foo=0Abar?='
+ self.getPage('/headers/Host', headers=[('Host', encoded)])
+ self.assertBody('foobar')
+
def test_basic_HTTPMethods(self):
helper.webtest.methods_with_bodies = ('POST', 'PUT', 'PROPFIND',
'PATCH')