Skip to content

Commit b4a65a4

Browse files
BSmick6claude
andcommitted
fix(security): deduplicate per-host and per-origin rejection log warnings
Log each unique rejected Host/Origin value only once per middleware instance to prevent log flooding in reverse-proxy deployments where a misconfigured client hammers the server repeatedly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5462caa commit b4a65a4

2 files changed

Lines changed: 29 additions & 2 deletions

File tree

src/mcp/server/transport_security.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class TransportSecurityMiddleware:
5151
def __init__(self, settings: TransportSecuritySettings | None = None):
5252
# If not specified, disable DNS rebinding protection by default for backwards compatibility
5353
self.settings = settings or TransportSecuritySettings(enable_dns_rebinding_protection=False)
54+
self._warned_hosts: set[str | None] = set()
55+
self._warned_origins: set[str] = set()
5456

5557
def _validate_host(self, host: str | None) -> bool:
5658
"""Validate the Host header against allowed values."""
@@ -71,7 +73,9 @@ def _validate_host(self, host: str | None) -> bool:
7173
if host.startswith(base_host + ":"):
7274
return True
7375

74-
logger.warning(f"Invalid Host header: {host}")
76+
if host not in self._warned_hosts:
77+
self._warned_hosts.add(host)
78+
logger.warning(f"Invalid Host header: {host}")
7579
return False
7680

7781
def _validate_origin(self, origin: str | None) -> bool:
@@ -93,7 +97,9 @@ def _validate_origin(self, origin: str | None) -> bool:
9397
if origin.startswith(base_origin + ":"):
9498
return True
9599

96-
logger.warning(f"Invalid Origin header: {origin}")
100+
if origin not in self._warned_origins:
101+
self._warned_origins.add(origin)
102+
logger.warning(f"Invalid Origin header: {origin}")
97103
return False
98104

99105
def _validate_content_type(self, content_type: str | None) -> bool:

tests/server/test_transport_security.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ def test_validate_host_port_wildcard_no_port() -> None:
8383
assert m._validate_host("localhost") is False
8484

8585

86+
def test_validate_host_logs_once_per_unique_host(caplog: pytest.LogCaptureFixture) -> None:
87+
m = TransportSecurityMiddleware(TransportSecuritySettings(allowed_hosts=["example.com"]))
88+
with caplog.at_level(logging.WARNING, logger="mcp.server.transport_security"):
89+
m._validate_host("evil.com")
90+
m._validate_host("evil.com")
91+
m._validate_host("evil.com")
92+
m._validate_host("other.com")
93+
host_records = [r for r in caplog.records if "Invalid Host header" in r.message]
94+
assert len(host_records) == 2 # one for evil.com, one for other.com
95+
96+
8697
# ---------------------------------------------------------------------------
8798
# TransportSecurityMiddleware._validate_origin
8899
# ---------------------------------------------------------------------------
@@ -113,6 +124,16 @@ def test_validate_origin_port_wildcard_different_base() -> None:
113124
assert m._validate_origin("http://other:3000") is False
114125

115126

127+
def test_validate_origin_logs_once_per_unique_origin(caplog: pytest.LogCaptureFixture) -> None:
128+
m = TransportSecurityMiddleware(TransportSecuritySettings(allowed_origins=["http://example.com"]))
129+
with caplog.at_level(logging.WARNING, logger="mcp.server.transport_security"):
130+
m._validate_origin("http://evil.com")
131+
m._validate_origin("http://evil.com")
132+
m._validate_origin("http://other.com")
133+
origin_records = [r for r in caplog.records if "Invalid Origin header" in r.message]
134+
assert len(origin_records) == 2 # one for evil.com, one for other.com
135+
136+
116137
# ---------------------------------------------------------------------------
117138
# TransportSecurityMiddleware.validate_request
118139
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)