Advocate
A drop-in wrapper around Python's requests library that blocks SSRF attacks by validating every socket connection before it opens.
Repository Health
Technical Analysis
Advocate is a set of tools built around the requests library for safely making HTTP requests on behalf of a third party. It exists to prevent server-side request forgery (SSRF) attacks by inspecting the actual address that requests is about to connect to — not just the URL string — and rejecting connections to private, loopback, link-local, multicast, or otherwise internal-looking addresses.
Because Advocate hooks into the same getaddrinfo call that requests/urllib3 uses to open the socket, it avoids the DNS-rebinding class of bugs that plague URL-parsing-based SSRF filters: the address it validates is guaranteed to be the address it connects to. Advocate is a mostly drop-in replacement for requests — swap the import and most code keeps working — with a configurable AddrValidator for IP/hostname/port allow- and block-lists. The project is explicitly unmaintained, so it should be evaluated as a reference implementation or forked rather than used as-is in new production systems.
What You Get
- A near drop-in replacement for
requests—advocate.get(),advocate.Session(), and the rest of the familiar API surface - An
AddrValidatorclass for configuring IP/hostname/port allow- and block-lists, with sane secure defaults (blocks private, loopback, link-local, multicast, and reserved ranges) - Single-lookup validation that checks the same
getaddrinforesult it connects with, closing the classic DNS-rebinding gap between check-time and connect-time - Automatic redirect inspection — every hop in a redirect chain is validated, not just the initial URL
- A
RequestsAPIWrapperhelper for sharing one validator config across an app without threading avalidatorkwarg through every call site - Optional
requests-futuresintegration viaadvocate.futures.FuturesSessionfor async-friendly code
Common Use Cases
- Fetching a URL supplied by an untrusted user or third-party integration (webhooks, link unfurling, avatar/image fetchers,
og:imagescrapers) - Building a webhook-delivery or callback system that must not be tricked into hitting internal metadata endpoints or admin panels
- Any server-side “fetch this URL for me” feature (PDF renderers, screenshot services, RSS readers, SSRF-prone import tools) that accepts attacker-influenced input
- Auditing or reference implementation work: studying a well-documented, single-getaddrinfo-call approach to SSRF prevention before rolling your own
Under The Hood
Architecture Advocate layers into requests at the socket-connection level rather than the URL-parsing level: advocate.Session subclasses requests.Session, locks down mount() to prevent adapters from being swapped out, and installs a ValidatingHTTPAdapter (adapters.py) for both http:// and https://. That adapter builds a ValidatingPoolManager (poolmanager.py) whose connection classes override _new_conn to call validating_create_connection (connection.py), which performs a single getaddrinfo call, runs the resolved address through AddrValidator.is_addrinfo_allowed (addrvalidator.py), and only then opens the socket — so the address that’s checked is guaranteed to be the address that’s connected to, closing the classic DNS-rebinding TOCTOU gap. Redirects are re-validated on every hop because they flow back through the same adapter.
Tech Stack Pure Python (100%), targeting CPython 3.6+. Runtime dependencies are requests (2.18–3.0), urllib3 (1.22–2.0), and netifaces for local-interface autodetection; requests-futures is an optional soft dependency wrapped in advocate/futures.py. No build step beyond a standard setup.py/setuptools packaging; no async runtime, no compiled extensions.
Code Quality The package is small (~1,600 lines including tests) and the core validation logic in addrvalidator.py and connection.py is heavily commented, explaining the reasoning behind each blocked IP class (link-local, 6to4 relay, Teredo, DNS64, site-local) with citations to relevant RFCs and prior SSRF research. test/test_advocate.py is a substantial 699-line suite exercising the requests test suite plus Advocate-specific validator behavior, run via pytest-cov. Naming and exception hierarchy (AdvocateException subclasses) are consistent, though a few TODOs remain unresolved (e.g. hostname-blacklist pattern normalization) and IPv6 support is explicitly flagged in the README as a work in progress.
API Design The public API is deliberately shaped to mirror requests — advocate.get/post/put/session() and advocate.Session() — so adopting it in an existing codebase is close to a one-line import swap. Configuration is centralized in a single AddrValidator object passed via a validator kwarg, and RequestsAPIWrapper lets a team bake one validator config into a reusable module-level advocate-like object instead of repeating the kwarg everywhere. The tradeoff for that low-friction API is thin documentation beyond the README — there’s no dedicated docs site, and API reference detail lives in docstrings and the README’s example blocks.