httpx-secure


Namehttpx-secure JSON
Version 1.1.0 PyPI version JSON
download
home_pageNone
SummaryDrop-in SSRF protection for httpx
upload_time2025-08-16 07:45:53
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords anyio async asyncio client dns dns-cache http httpx security ssrf ssrf-protection trio web-security
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # httpx-secure

[![PyPI - Python Version](https://shields.monicz.dev/pypi/pyversions/httpx-secure)](https://pypi.org/p/httpx-secure)
[![Liberapay Patrons](https://shields.monicz.dev/liberapay/patrons/Zaczero?logo=liberapay&label=Patrons)](https://liberapay.com/Zaczero/)
[![GitHub Sponsors](https://shields.monicz.dev/github/sponsors/Zaczero?logo=github&label=Sponsors&color=%23db61a2)](https://github.com/sponsors/Zaczero)

Drop-in SSRF protection for httpx.

## Why Use This?

- **SSRF Protection**: Block requests to private/internal IP addresses
- **Custom Validation**: Extend with your own validation logic
- **Minimal Overhead**: Efficient implementation with built-in DNS caching
- **Broad Python Support**: Compatible with Python 3.9+
- [**Semantic Versioning**](https://semver.org): Predictable, reliable updates
- [**Zero-Clause BSD**](https://choosealicense.com/licenses/0bsd/): Public domain, use freely anywhere

## Installation

```bash
pip install httpx-secure
```

## Quick Start

```python
import httpx
from httpx_secure import httpx_ssrf_protection

client = httpx_ssrf_protection(
    httpx.AsyncClient(),
    dns_cache_size=1000,  # Cache up to 1000 DNS resolutions
    dns_cache_ttl=300,    # Cache for 5 minutes
)

await client.get("https://public.domain")   # Allowed
await client.get("https://private.domain")  # Blocked
```

## Custom Validation

For example, implement a simple domain whitelist to restrict requests to specific hosts:

```python
import httpx
from httpx_secure import httpx_ssrf_protection
from ipaddress import IPv4Address, IPv6Address

def custom_validator(
    hostname: str,
    ip: IPv4Address | IPv6Address,
    port: int
) -> bool:
    return hostname in {
        "whitelisted.domain",
        "webhook.partner.com",
    }

client = httpx_ssrf_protection(
    httpx.AsyncClient(),
    custom_validator=custom_validator,
)

await client.get("https://whitelisted.domain")  # Allowed
await client.get("https://unknown.domain")      # Blocked
```

## How It Works

1. **Cache Lookup**: First checks if the host has been recently validated and cached
2. **DNS Resolution**: If not cached, resolves the hostname to an IP address
3. **Validation**: Verifies the IP is globally routable, blocking private/internal addresses
4. **Custom Validation**: If provided, your custom validator is called for additional checks
5. **Request Modification**: Rewrites the request to use the validated IP directly

The DNS cache significantly reduces latency for repeated requests, while per-host locking ensures efficient concurrent resolution of parallel requests.

> [!TIP]
> The SSRF protection applies to all HTTP methods (GET, POST, PUT, DELETE, etc.) and automatically validates redirects to prevent SSRF attacks through redirect chains.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "httpx-secure",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "anyio, async, asyncio, client, dns, dns-cache, http, httpx, security, ssrf, ssrf-protection, trio, web-security",
    "author": null,
    "author_email": "Kamil Monicz <kamil@monicz.dev>",
    "download_url": "https://files.pythonhosted.org/packages/7d/d9/1df92f016d9bbfb97a84c63f427cc7c1c22ec68653eff85f1979e677e5a3/httpx_secure-1.1.0.tar.gz",
    "platform": null,
    "description": "# httpx-secure\n\n[![PyPI - Python Version](https://shields.monicz.dev/pypi/pyversions/httpx-secure)](https://pypi.org/p/httpx-secure)\n[![Liberapay Patrons](https://shields.monicz.dev/liberapay/patrons/Zaczero?logo=liberapay&label=Patrons)](https://liberapay.com/Zaczero/)\n[![GitHub Sponsors](https://shields.monicz.dev/github/sponsors/Zaczero?logo=github&label=Sponsors&color=%23db61a2)](https://github.com/sponsors/Zaczero)\n\nDrop-in SSRF protection for httpx.\n\n## Why Use This?\n\n- **SSRF Protection**: Block requests to private/internal IP addresses\n- **Custom Validation**: Extend with your own validation logic\n- **Minimal Overhead**: Efficient implementation with built-in DNS caching\n- **Broad Python Support**: Compatible with Python 3.9+\n- [**Semantic Versioning**](https://semver.org): Predictable, reliable updates\n- [**Zero-Clause BSD**](https://choosealicense.com/licenses/0bsd/): Public domain, use freely anywhere\n\n## Installation\n\n```bash\npip install httpx-secure\n```\n\n## Quick Start\n\n```python\nimport httpx\nfrom httpx_secure import httpx_ssrf_protection\n\nclient = httpx_ssrf_protection(\n    httpx.AsyncClient(),\n    dns_cache_size=1000,  # Cache up to 1000 DNS resolutions\n    dns_cache_ttl=300,    # Cache for 5 minutes\n)\n\nawait client.get(\"https://public.domain\")   # Allowed\nawait client.get(\"https://private.domain\")  # Blocked\n```\n\n## Custom Validation\n\nFor example, implement a simple domain whitelist to restrict requests to specific hosts:\n\n```python\nimport httpx\nfrom httpx_secure import httpx_ssrf_protection\nfrom ipaddress import IPv4Address, IPv6Address\n\ndef custom_validator(\n    hostname: str,\n    ip: IPv4Address | IPv6Address,\n    port: int\n) -> bool:\n    return hostname in {\n        \"whitelisted.domain\",\n        \"webhook.partner.com\",\n    }\n\nclient = httpx_ssrf_protection(\n    httpx.AsyncClient(),\n    custom_validator=custom_validator,\n)\n\nawait client.get(\"https://whitelisted.domain\")  # Allowed\nawait client.get(\"https://unknown.domain\")      # Blocked\n```\n\n## How It Works\n\n1. **Cache Lookup**: First checks if the host has been recently validated and cached\n2. **DNS Resolution**: If not cached, resolves the hostname to an IP address\n3. **Validation**: Verifies the IP is globally routable, blocking private/internal addresses\n4. **Custom Validation**: If provided, your custom validator is called for additional checks\n5. **Request Modification**: Rewrites the request to use the validated IP directly\n\nThe DNS cache significantly reduces latency for repeated requests, while per-host locking ensures efficient concurrent resolution of parallel requests.\n\n> [!TIP]\n> The SSRF protection applies to all HTTP methods (GET, POST, PUT, DELETE, etc.) and automatically validates redirects to prevent SSRF attacks through redirect chains.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Drop-in SSRF protection for httpx",
    "version": "1.1.0",
    "project_urls": {
        "Issues": "https://github.com/Zaczero/httpx-secure/issues",
        "Repository": "https://github.com/Zaczero/httpx-secure"
    },
    "split_keywords": [
        "anyio",
        " async",
        " asyncio",
        " client",
        " dns",
        " dns-cache",
        " http",
        " httpx",
        " security",
        " ssrf",
        " ssrf-protection",
        " trio",
        " web-security"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "2bd1403e0be9de27d62438cf729d9038e8f48400a18d3b6eab60bcf9f2d6441f",
                "md5": "5b3e7795971b0db972f8dff505a68dcb",
                "sha256": "0952a1a33a74018ebe9b1f29fd395c250ab4cc333a0822bf8b62a9659a578ab9"
            },
            "downloads": -1,
            "filename": "httpx_secure-1.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5b3e7795971b0db972f8dff505a68dcb",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 5565,
            "upload_time": "2025-08-16T07:45:52",
            "upload_time_iso_8601": "2025-08-16T07:45:52.558790Z",
            "url": "https://files.pythonhosted.org/packages/2b/d1/403e0be9de27d62438cf729d9038e8f48400a18d3b6eab60bcf9f2d6441f/httpx_secure-1.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7dd91df92f016d9bbfb97a84c63f427cc7c1c22ec68653eff85f1979e677e5a3",
                "md5": "3779dd730cbeb31dd2e78931ea5e5185",
                "sha256": "883a075a6024667b475554dc82c18496259f50251b23448d55712cc6bd0bc2ae"
            },
            "downloads": -1,
            "filename": "httpx_secure-1.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "3779dd730cbeb31dd2e78931ea5e5185",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 18731,
            "upload_time": "2025-08-16T07:45:53",
            "upload_time_iso_8601": "2025-08-16T07:45:53.979964Z",
            "url": "https://files.pythonhosted.org/packages/7d/d9/1df92f016d9bbfb97a84c63f427cc7c1c22ec68653eff85f1979e677e5a3/httpx_secure-1.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-16 07:45:53",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Zaczero",
    "github_project": "httpx-secure",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "httpx-secure"
}
        
Elapsed time: 0.70889s