Skip to content

[Security] HTTP fallback path bypasses DNS-rebinding IP pinning when urllib3 unavailable #2050

@brendancol

Description

@brendancol

Summary

_HTTPSource in xrspatial/geotiff/_reader.py has two transport paths for HTTP range fetches: a urllib3 path and a stdlib urllib.request fallback. Only the urllib3 path pins the TCP connection to the IP returned by _validate_http_url, which is what closes the DNS-rebinding TOCTOU window from issue #1846. The stdlib fallback calls urllib.request.Request(self._url), which re-resolves the hostname at request time, so the IP the validator approved no longer guards the connection.

urllib3 is not listed under install_requires in setup.cfg. A production install that does not pull urllib3 in transitively will run the unpinned stdlib path and silently lose the #1846 mitigation.

Affected code

  • xrspatial/geotiff/_reader.py:1048, _HTTPSource.read_range stdlib fallback (urllib.request.Request(self._url, ...) via _get_stdlib_opener)
  • xrspatial/geotiff/_reader.py:1222, _HTTPSource.read_all stdlib fallback, same pattern
  • xrspatial/geotiff/_reader.py:344, _get_http_pool swallows ImportError and returns None, which routes callers into the stdlib fallback when urllib3 is missing
  • setup.cfg:21, install_requires does not list urllib3

Why it matters

_validate_http_url resolves the hostname and rejects addresses on loopback, link-local, or private space. Without IP pinning, a hostile resolver can return a public IP during validation and a private IP at connect time, and the check fails. Without urllib3 in install_requires, a real install never reaches the pinned path.

Proposed fix

  1. Add urllib3 to install_requires in setup.cfg.
  2. Remove the stdlib fallback in _HTTPSource.read_range and _HTTPSource.read_all.
  3. Simplify _get_http_pool so it no longer catches ImportError.
  4. Delete _get_stdlib_opener and _ValidatingRedirectHandler once nothing calls them.
  5. Drop the test_stdlib_* cases in test_ssrf_hardening_1664.py. The test_urllib3_* cases in the same file already cover redirect re-validation for the path that remains.

Scope

Finding 1 only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions