From c734f873270cf9ca414832423f7aad98443c379f Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Thu, 9 Jan 2020 11:26:24 +0100 Subject: [PATCH] CVE-2018-20060 --- poolmanager.py | 11 ++++++++++- util/retry.py | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/poolmanager.py b/poolmanager.py index 4ae9174..bfa5115 100644 --- a/poolmanager.py +++ b/poolmanager.py @@ -312,8 +312,9 @@ class PoolManager(RequestMethods): kw['assert_same_host'] = False kw['redirect'] = False + if 'headers' not in kw: - kw['headers'] = self.headers + kw['headers'] = self.headers.copy() if self.proxy is not None and u.scheme == "http": response = conn.urlopen(method, url, **kw) @@ -335,6 +336,14 @@ class PoolManager(RequestMethods): if not isinstance(retries, Retry): retries = Retry.from_int(retries, redirect=redirect) + # Strip headers marked as unsafe to forward to the redirected location. + # Check remove_headers_on_redirect to avoid a potential network call within + # conn.is_same_host() which may use socket.gethostbyname() in the future. + if (retries.remove_headers_on_redirect + and not conn.is_same_host(redirect_location)): + for header in retries.remove_headers_on_redirect: + kw['headers'].pop(header, None) + try: retries = retries.increment(method, url, response=response, _pool=conn) except MaxRetryError: diff --git a/util/retry.py b/util/retry.py index c603cb4..0b83963 100644 --- a/util/retry.py +++ b/util/retry.py @@ -126,6 +126,11 @@ class Retry(object): exhausted, to raise a MaxRetryError, or to return a response with a response code in the 3xx range. + :param iterable remove_headers_on_redirect: + Sequence of headers to remove from the request when a response + indicating a redirect is returned before firing off the redirected + request + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: whether we should raise an exception, or return a response, if status falls in ``status_forcelist`` range and retries have @@ -144,6 +149,8 @@ class Retry(object): DEFAULT_METHOD_WHITELIST = frozenset([ 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) + DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(['Authorization']) + RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) #: Maximum backoff time. @@ -152,7 +159,8 @@ class Retry(object): def __init__(self, total=10, connect=None, read=None, redirect=None, status=None, method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, backoff_factor=0, raise_on_redirect=True, raise_on_status=True, - history=None, respect_retry_after_header=True): + history=None, respect_retry_after_header=True, + remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST): self.total = total self.connect = connect @@ -171,6 +179,7 @@ class Retry(object): self.raise_on_status = raise_on_status self.history = history or tuple() self.respect_retry_after_header = respect_retry_after_header + self.remove_headers_on_redirect = remove_headers_on_redirect def new(self, **kw): params = dict( @@ -182,6 +191,7 @@ class Retry(object): raise_on_redirect=self.raise_on_redirect, raise_on_status=self.raise_on_status, history=self.history, + remove_headers_on_redirect=self.remove_headers_on_redirect, ) params.update(kw) return type(self)(**params) -- 2.24.1