Raise bad request when receiving HTTP request from untrusted proxy (#51839)

* Raise bad request when receiving HTTP request from untrusted proxy

* Fix code comment
This commit is contained in:
Franck Nijhof 2021-06-17 04:41:19 +02:00 committed by GitHub
parent 17414439df
commit 33e08f38da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 11 additions and 89 deletions

View File

@ -47,7 +47,8 @@ def async_setup_forwarded(
Additionally:
- If no X-Forwarded-For header is found, the processing of all headers is skipped.
- Log a warning when untrusted connected peer provides X-Forwarded-For headers.
- Throw HTTP 400 status when untrusted connected peer provides
X-Forwarded-For headers.
- If multiple instances of X-Forwarded-For, X-Forwarded-Proto or
X-Forwarded-Host are found, an HTTP 400 status code is thrown.
- If malformed or invalid (IP) data in X-Forwarded-For header is found,
@ -87,26 +88,20 @@ def async_setup_forwarded(
# We have X-Forwarded-For, but config does not agree
if not use_x_forwarded_for:
_LOGGER.warning(
_LOGGER.error(
"A request from a reverse proxy was received from %s, but your "
"HTTP integration is not set-up for reverse proxies; "
"This request will be blocked in Home Assistant 2021.7 unless "
"you configure your HTTP integration to allow this header",
"HTTP integration is not set-up for reverse proxies",
connected_ip,
)
# Block this request in the future, for now we pass.
return await handler(request)
raise HTTPBadRequest
# Ensure the IP of the connected peer is trusted
if not any(connected_ip in trusted_proxy for trusted_proxy in trusted_proxies):
_LOGGER.warning(
"Received X-Forwarded-For header from untrusted proxy %s, headers not processed; "
"This request will be blocked in Home Assistant 2021.7 unless you configure "
"your HTTP integration to allow this proxy to reverse your Home Assistant instance",
_LOGGER.error(
"Received X-Forwarded-For header from an untrusted proxy %s",
connected_ip,
)
# Not trusted, Block this request in the future, continue as normal
return await handler(request)
raise HTTPBadRequest
# Multiple X-Forwarded-For headers
if len(forwarded_for_headers) > 1:

View File

@ -33,9 +33,9 @@ async def test_x_forwarded_for_without_trusted_proxy(aiohttp_client, caplog):
mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"})
assert resp.status == 200
assert resp.status == 400
assert (
"Received X-Forwarded-For header from untrusted proxy 127.0.0.1, headers not processed"
"Received X-Forwarded-For header from an untrusted proxy 127.0.0.1"
in caplog.text
)
@ -103,35 +103,13 @@ async def test_x_forwarded_for_disabled_with_proxy(aiohttp_client, caplog):
mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"})
assert resp.status == 200
assert resp.status == 400
assert (
"A request from a reverse proxy was received from 127.0.0.1, but your HTTP "
"integration is not set-up for reverse proxies" in caplog.text
)
async def test_x_forwarded_for_with_untrusted_proxy(aiohttp_client):
"""Test that we get the IP from transport with untrusted proxy."""
async def handler(request):
url = mock_api_client.make_url("/")
assert request.host == f"{url.host}:{url.port}"
assert request.scheme == "http"
assert not request.secure
assert request.remote == "127.0.0.1"
return web.Response()
app = web.Application()
app.router.add_get("/", handler)
async_setup_forwarded(app, True, [ip_network("1.1.1.1")])
mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"})
assert resp.status == 200
async def test_x_forwarded_for_with_spoofed_header(aiohttp_client):
"""Test that we get the IP from the transport with a spoofed header."""
@ -205,31 +183,6 @@ async def test_x_forwarded_for_with_multiple_headers(aiohttp_client, caplog):
assert "Too many headers for X-Forwarded-For" in caplog.text
async def test_x_forwarded_proto_without_trusted_proxy(aiohttp_client):
"""Test that proto header is ignored when untrusted."""
async def handler(request):
url = mock_api_client.make_url("/")
assert request.host == f"{url.host}:{url.port}"
assert request.scheme == "http"
assert not request.secure
assert request.remote == "127.0.0.1"
return web.Response()
app = web.Application()
app.router.add_get("/", handler)
async_setup_forwarded(app, True, [])
mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get(
"/", headers={X_FORWARDED_FOR: "255.255.255.255", X_FORWARDED_PROTO: "https"}
)
assert resp.status == 200
@pytest.mark.parametrize(
"x_forwarded_for,remote,x_forwarded_proto,secure",
[
@ -409,32 +362,6 @@ async def test_x_forwarded_proto_incorrect_number_of_elements(
)
async def test_x_forwarded_host_without_trusted_proxy(aiohttp_client):
"""Test that host header is ignored when untrusted."""
async def handler(request):
url = mock_api_client.make_url("/")
assert request.host == f"{url.host}:{url.port}"
assert request.scheme == "http"
assert not request.secure
assert request.remote == "127.0.0.1"
return web.Response()
app = web.Application()
app.router.add_get("/", handler)
async_setup_forwarded(app, True, [])
mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get(
"/",
headers={X_FORWARDED_FOR: "255.255.255.255", X_FORWARDED_HOST: "example.com"},
)
assert resp.status == 200
async def test_x_forwarded_host_with_trusted_proxy(aiohttp_client):
"""Test that we get the host header if proxy is trusted."""