Skip to content

Real IP detection in create_session() uses unprotected direct request #4

Description

@ponich

Problem

In create_session(), the real IP is detected via a direct (non-proxied) request (__init__.py:140):

real_ip = requests.get(ip_check_url, timeout=10).text.strip()

This is a plain requests.get() — not through the SafeSession, no proxy, no protection. This is by design (you need to know your real IP to detect leaks later), but it has implications:

1. Real IP is sent to a third-party service

The user's real IP is sent to api.ipify.org (or whatever ip_check_url is configured). If the user's threat model includes hiding their real IP from all third parties, this is a leak at session creation time.

2. No validation of the response

The response from ipify.org is trusted blindly:

  • No validation that the response looks like an IP address
  • No HTTPS certificate pinning
  • A MITM attack or DNS poisoning could return a fake IP, causing check_ip() to never detect a real leak (because real_ip would be wrong)

3. Request could fail, exposing the user

If ipify.org is down or blocked, create_session() raises an unhandled RequestException. This is fine — the session isn't created. But the error message might expose network details in logs.

Proposed solution

Validate the real IP response

import ipaddress

raw = requests.get(ip_check_url, timeout=10).text.strip()
try:
    ipaddress.ip_address(raw)
except ValueError:
    raise ProxyError(f"IP check returned invalid response: {raw!r}")
real_ip = raw

Allow the user to provide their real IP directly

For users who already know their real IP or don't want to send it to a third party:

def create_session(
    ...,
    real_ip: str | None = None,  # skip auto-detection if provided
) -> SafeSession:
    if real_ip is None:
        real_ip = requests.get(ip_check_url, timeout=10).text.strip()
    ...

Document the direct request

Make it clear in the README that create_session() makes one direct (non-proxied) HTTP request to determine the real IP.

Impact

  • Severity: Low — by design, but undocumented trade-off
  • Scope: Every call to create_session()
  • Claim affected: Users expecting zero direct requests may be surprised

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions