Skip to main content

Build Status Coverage Status PyPI version Code style: black

TorIpChanger

A simple workaround for Tor IP changing behavior:

An important thing to note is that a new circuit does not necessarily mean a new IP address. Paths are randomly selected based on heuristics like speed and stability. There are only so many large exits in the Tor network, so it's not uncommon to reuse an exit you have had previously.

Installation

pip install toripchanger

Dependencies

TorIpChanger assumes you have installed and setup Tor and Privoxy, for example following steps mentioned in these tutorials:

Or, when using Docker, simply use https://github.com/dperson/torproxy. Refer to Dockerfile and docker-compose.yaml for more details.

dm@lnx:~/code/toripchanger$ docker-compose up -d
Starting toripchanger_tor-proxy_1 ... done
Starting toripchanger_tor-ip-changer_1 ... done
dm@lnx:~/code/toripchanger$ curl http://localhost:8080/changeip/
{"error":"","newIp":"1.2.3.4"}

Usage

Basic examples

With TorIpChanger you can define how often a Tor IP can be reused:

from toripchanger import TorIpChanger

# Tor IP reuse is prohibited.
tor_ip_changer_0 = TorIpChanger(reuse_threshold=0)
current_ip = tor_ip_changer_0.get_new_ip()

# Current Tor IP address can be reused after one other IP was used (default setting).
tor_ip_changer_1 = TorIpChanger(local_http_proxy='http://127.0.0.1:8888')
current_ip = tor_ip_changer_1 .get_new_ip()

# Current Tor IP address can be reused after 5 other Tor IPs were used.
tor_ip_changer_5 = TorIpChanger(tor_address="localhost", reuse_threshold=5)
current_ip = tor_ip_changer_5.get_new_ip()

Remote Tor control

Sometimes, typically while using Docker, you may want to control a Tor instance which doesn't run on localhost. To do this, you have two options.

Use 0.0.0.0 as control address

Set ControlPort to 0.0.0.0:9051 in your torrc file and set tor_address when initializing TorIpChanger

from toripchanger import TorIpChanger

# Mirroring the setup from the local docker-compose.yaml.
tor_ip_changer = TorIpChanger(
  tor_password="tor",
  tor_port=9051,
  tor_address="tor-proxy",
  local_http_proxy='http://tor-proxy:8118',
)
current_ip = tor_ip_changer.get_new_ip()

Though, Tor is not very happy about it (and rightly so) and will warn you

You have a ControlPort set to accept connections from a non-local address. This means that programs not running on your computer can reconfigure your Tor. That's pretty bad, since the controller protocol isn't encrypted! Maybe you should just listen on 127.0.0.1 and use a tool like stunnel or ssh to encrypt remote connections to your control port.

Also, you have to set either CookieAuthentication or HashedControlPassword otherwise ControlPort will be closed

You have a ControlPort set to accept unauthenticated connections from a non-local address. This means that programs not running on your computer can reconfigure your Tor, without even having to guess a password. That's so bad that I'm closing your ControlPort for you. If you need to control your Tor remotely, try enabling authentication and using a tool like stunnel or ssh to encrypt remote access.

Please note ControlListenAddress config is OBSOLETE and Tor (tested with 0.3.3.7) will ignore it and log the following message

[warn] Skipping obsolete configuration option 'ControlListenAddress'

While the config itself is obsolte, its documentation (not the official documentation!) concerning the risks related to exposing ControlPort on 0.0.0.0 is still valid

We strongly recommend that you leave this alone unless you know what you’re doing, since giving attackers access to your control listener is really dangerous.

Use toripchanger_server

toripchanger_server script starts a simple web server which allows you to change Tor' IP remotely using an HTTP get request to /changeip/. The response body is always

{
    "newIp": "1.2.3.4",
    "error": ""
}

with an appropriate status (error is an empty string when all is good).

Changing Tor' IP may not be instantaneous (especially when combined with a high reuse_threshold) and hence your client should use a reasonable timeout (e.g. at least 60s).

toripchanger_server takes all arguments required to initialize TorIpChanger plus --server-host and --server-port, for more details see the usage below.

usage: toripchanger_server [-h] [--server-host SERVER_HOST]
                           [--server-port SERVER_PORT]
                           [--reuse-threshold REUSE_THRESHOLD]
                           [--local-http-proxy LOCAL_HTTP_PROXY]
                           [--tor-password TOR_PASSWORD]
                           [--tor-address TOR_ADDRESS] [--tor-port TOR_PORT]
                           [--new-ip-max-attempts NEW_IP_MAX_ATTEMPTS]

optional arguments:
  -h, --help            show this help message and exit
  --server-host SERVER_HOST
                        TorIpChanger server host (default: 0.0.0.0)
  --server-port SERVER_PORT
                        TorIpChanger server port (default: 8080)
  --reuse-threshold REUSE_THRESHOLD
                        Number of IPs to use before reusing the current one
                        (default: 1)
  --local-http-proxy LOCAL_HTTP_PROXY
                        Local proxy IP and port (default: http://127.0.0.1:8118)
  --tor-password TOR_PASSWORD
                        Tor controller password (default: "")
  --tor-address TOR_ADDRESS
                        IP address or resolvable hostname of the Tor
                        controller (default: 127.0.0.1)
  --tor-port TOR_PORT   Port number of the Tor controller (default: 9051)
  --new-ip-max-attempts NEW_IP_MAX_ATTEMPTS
                        Get new IP attempts limit (default: 10)

To be able to change Tor IP remotely with toripchanger_server

  1. run pip install toripchanger[server] in your container
  2. start toripchanger_server (on the same host where Tor runs)
  3. expose the port toripchanger_server runs on to Docker host (or other containers)
  4. test changing IP works, e.g. curl http://localhost:8080/changeip/

An example docker-compose.yaml can be used for testing as instructed in section Dependencies.

Metadata

Release files for toripchanger 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for toripchanger 1.3.0
File Size Uploaded Provenance Actions
toripchanger-1.3.0.tar.gz 13.1 kB Without provenance Details for toripchanger-1.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for toripchanger 1.3.0
File Interpreter ABI Platform Actions
toripchanger-1.3.0-py3-none-any.whl Python 3 none any Details for toripchanger-1.3.0-py3-none-any.whl

Total release size: 22.1 kB

Release files / toripchanger-1.3.0.tar.gz

Download URL toripchanger-1.3.0.tar.gz
Size 13.1 kB
Tags Source
SHA-256 checksum
How to use checksums
15849d23b736a4bab32937d01d2354c5eef94f80de9cde34913b96d8e788738d
BLAKE2b-256 checksum
How to use checksums
6a5ffa85e2a9b58012b765dd0714cd8899b39849b087fcbe5ce752ad278c4f67
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.20

Release files / toripchanger-1.3.0-py3-none-any.whl

Download URL toripchanger-1.3.0-py3-none-any.whl
Size 9.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d079824eedb3cf391f226a04b33c0069da32baf692eb6c3321dd1f0b7ece1728
BLAKE2b-256 checksum
How to use checksums
6d605078f4ed037f29beec71f22d0c9df00484821bc0b3da1e1663c593d06efa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.20

Security for toripchanger 1.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page