Skip to content

DNS rebinding :* allowlist matches Host/Origin suffixes that are not ports #3463

Open

Description

@Oskii

What happened

TransportSecurityMiddleware treats allowed_hosts / allowed_origins entries that end in :* as a prefix match: value.startswith(base + ":").

With allowed_hosts=["127.0.0.1:*"] or ["wild.example:*"], these Host values are accepted today:

  • 127.0.0.1:8080.evil
  • wild.example:9000.evil

The same pattern accepts Origin http://wild.example:9000.evil for http://wild.example:*.

Existing tests only cover a numeric port (wild.example:9000). They do not cover a suffix after the port.

What I expected

base:* should mean base plus a numeric port, not any string that starts with base:.

How to reproduce

On main @ 08a3bc8:

from mcp.server.transport_security import TransportSecurityMiddleware, TransportSecuritySettings
from starlette.requests import Request

settings = TransportSecuritySettings(
    enable_dns_rebinding_protection=True,
    allowed_hosts=["wild.example:*"],
    allowed_origins=["http://wild.example:*"],
)
mw = TransportSecurityMiddleware(settings)
req = Request({"type": "http", "method": "GET", "headers": [(b"host", b"wild.example:9000.evil")]})
# validate_request returns None (accept). I expected 421.

I can send a PR that requires the suffix after base: to be digits, plus tests for the suffix cases. Happy to do that if you want it.

Written with AI assistance. I read the matcher next to tests/server/test_transport_security.py and reproduced it locally.

Activity

  1. Oskii commented on Sep 6, 2026

    @Oskii
    Author

    I reported this and I am happy to keep the fix on fix/dns-rebinding-wildcard-port (PR 3465). The suffix cases are in tests/server/test_transport_security.py. I will not open a replacement PR.

    Written with AI assistance. I reproduced the Host wild.example:9000.evil case on 08a3bc8 before sending the patch.

  2. added
    v2Affects the v2 line (2.x on main)
    v1Affects the v1.x maintenance line
    on Sep 6, 2026
  3. Oskii commented on Sep 8, 2026

    @Oskii
    Author

    Confirmed still on main @ 9972c21.

    I ran the same Request / validate_request snippet from the issue body against current TransportSecurityMiddleware. Host wild.example:9000.evil with allowed_hosts=["wild.example:*"] still returns None. Origin http://wild.example:9000.evil with allowed_origins=["http://wild.example:*"] is accepted the same way.

    This matters if someone turns DNS rebinding protection on and uses :* to mean "this host, any port". A client that can set Host or Origin can satisfy the allowlist with a longer value, which is not a port.

    Intended behaviour: base:* should match only base: plus a numeric port. Empty port and a suffix like 9000.evil should fail.

    Approach I would take: one helper, base:* matches only base:<digits>, used by both Host and Origin. No new settings. Existing numeric-port cases stay accepted.

    I reported this and would like to keep the fix on the existing fix/dns-rebinding-wildcard-port branch rather than open another PR.

    Written with AI assistance. I re-read the matcher on 9972c21 and re-ran the suffix cases locally after merging that commit into the branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv1Affects the v1.x maintenance linev2Affects the v2 line (2.x on main)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions