mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
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:
parent
17414439df
commit
33e08f38da
@ -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:
|
||||
|
@ -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."""
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user