IP filtering is simply a mechanism that decides which types of IPs will be processed normally and which will be discarded. By discarded I mean rejected or completely ignored. You can apply many different sorts of criteria to determine which IPs you wish to filter, e.g. you can create a rule that only one POST request can come from one IP in one day, or you can put geographic restrictions based on incoming IPs.

Unfortunately, basic IP address filtering has always been far from perfect, and any user with some knowledge about proxies, tor or VPNs can easily bypass those restrictions and access resources otherwise not allowed for his original IP. Before you read on further, if you don’t understand the basics about Tor, have a look at the beginner’s guide to Tor on Ubuntu. All the codes mentioned below can be found here on my github repo.

EXAMPLE: One POST request per day based on IP

Let’s take an example of a website which allows users to submit a form, with the restriction that each IP can do it only once each day. Any submit request that originates more than once from the same IP during the same day will be rejected. So how can more than one submits be made by the same user?

Solution: We will rely on Tor, to make multiple submit requests, each with a different IP.

Lets assume the website we want to send multiple requests to has such a form:

<form method="post">

<input type="text" name="id" />

<input type="submit" name="submit" />

<input type="hidden" name="key" value="f8c86a4f223bdd" />

</form>

The key is the value of the “session” cookie set by the website.

Tor, Stem and Python

Follow all the installation instructions for Tor on Ubuntu given here.

Installing Stem (Python Controller Library)

To install stem you can either fetch it directly by apt-get:

$ sudo apt-get install python-stem

ALTERNATIVELY, if you love living on the bleeding edge, you can directly fetch it from Stem’s official git repository and install it:

$ git clone https://git.torproject.org/stem.git

$ python setup.py install

Writing a python script

Our goal:

i) Make a POST request

ii) Change our IP using tor

iii) Goto step one and repeat process

Please refer to this link to see the code.

Please refer to my github link to see the code.

If a NEWNYM signal is sent to the Tor controller, tor makes a new circuit, this can and cannot result in a new IP. This code will make 98 attempts to submit request, but not every request will be accepted, for the same reason we mentioned above i.e. we won’t be getting a new IP address each time we send a NEWNYM signal to the tor controller. Let’s break down this code and see what is happening:

First Block

#configuration start

id_number=40

votes=98

#configuration end

id_number is the id number for the text field required by the form, we are giving it a value of 40 here.

is the id number for the text field required by the form, we are giving it a value of 40 here. votes are the number of iterations to be made.

Second Block

with Controller.from_port(port = 9051) as controller: The first statement, defined the controller and its port (please note that you have to enable the control port in torrc config file, refer to the installation tutorial link mentioned above if you need help with that).

If you are unfamiliar with the with command:

Python’s with keyword is shorthand for a try/finally block. With a Controller the following:

with Controller.from_port(port = 9051) as controller:

#do my stuff

is equivalent to:

controller = Controller.from_port(port = 9051)

try:

#do my stuff

finally:

controller.close()

controller.authenticate(): This authenticates with the controller, depending upon if you set a cookie authentication method or a password method. Note if you set a password method to authenticate earlier, you should give it a password with this statement like this: controller.authenticate(‘passwordhere’).

Third Block

controller.signal(Signal.NEWNYM): sends a signal to the controller to create a new circuit. NEWNYM is the signal for that.

sends a signal to the controller to create a new circuit. NEWNYM is the signal for that. socks.set_default_proxy(socks.SOCKS5, SOCKS5_PROXY_HOST, SOCKS5_PROXY_PORT) : Sets a default proxy to tor which all further socksocket objects will use, unless explicitly changed.

: Sets a default proxy to tor which all further socksocket objects will use, unless explicitly changed. socket.socket = socks.socksocket: Create a new socket using the given address family, so that now the tor connection is used.

Fourth Block

cj = cookielib.CookieJar(): cj will be used to store cookies for the opener session.

The CookieJar class stores HTTP cookies. It extracts cookies from HTTP requests, and returns them in HTTP responses.

opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)): creates an opener. HTTPCookieProcessor is a class to handle cookies.

creates an opener. HTTPCookieProcessor is a class to handle cookies. opener.addheaders = … : if you want to change the default headers sent with the session and the post request later, this is where you need to change them. It could be that the server is programmed to decline any post requests from python or other languages, this is where you can change header to act as if the request is being originating from a browser.

: if you want to change the default headers sent with the session and the post request later, this is where you need to change them. It could be that the server is programmed to decline any post requests from python or other languages, this is where you can change header to act as if the request is being originating from a browser. session = opener.open(‘url.com’) : This opens a session with the url given. This is done to get the cookie value, and then later attach that cookie value with the post request.

: This opens a session with the url given. This is done to get the cookie value, and then later attach that cookie value with the post request. In the next steps, we check the status code of the requested session, if it is 200 (means it is OK), we get the cookie value stored in cj by the name of “session”. Then a dictionary is created with all the parameters, to be sent in the POST request. Then urllib.urlencode(params) is used to URL encode the dictionary.

The urlib.urlencode returns a string as an output. It takes a dictionary of key-value pairs and encodes them like a query in a URL string. The pairs are in key-value format and are delimited by ampersands.

Finally a POST request is made using the encoded parameters, and then right after the opener is closed.

With a few lines of code we can make sure that the post request is only made when a new ip is allocated by comparing the old ip and new ip, but for the sake of simplicity, I have not included it in the above code.