Jouer avec Tor et sa librairie python Stem

J’ai récemment voulu utiliser le réseau Tor en contrôlant mon nœud de sortie, avec l’API python Stem.

Mon objectif était de me connecter sur un site qui propose un service gratuit, mais uniquement si vous êtes nouveau (une IP qui n’a pas déjà bénéficié du service) et que vous résidez en Europe.

Pour ne pas être détecté comme un utilisateur de TOR sur le site, j’ai écrit un script python qui va récupérer la liste des relais et forcer la sortie du réseau par des relais récemment arrivés sur le réseau (uptime < 3h).

from stem.descriptor.remote import DescriptorDownloader
from stem.control import Controller
from stem import Signal
import os,pygeoip
 
path = os.path.dirname(os.path.realpath(__file__))+'/';
gi = pygeoip.GeoIP(path+'GeoIP.dat')
 
downloader = DescriptorDownloader(
  use_mirrors = True,
  timeout = 10,
)
 
good_country = ['FR','DE','GB','ES','IT','AT','BE','BG','HR','CY','CZ','DK','EE','FI','GR','HU','IE','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','SE']
 
nodes=[]
 
query = downloader.get_server_descriptors()
 
print 'Exit Relays:'
 
try:
    for desc in query.run():
        if desc.exit_policy.is_exiting_allowed() and desc.uptime <= (60*60*3) and desc.uptime >= (2*60) and desc.observed_bandwidth >= desc.average_bandwidth and desc.average_bandwidth >= 100000:
            cc = gi.country_code_by_addr(desc.address)
            if cc in good_country:
                nodes.append(desc.address)
                print '  %s (%s) :%d s (%d/%d/%d) bytes/s' % (cc, desc.address, desc.uptime, desc.average_bandwidth, desc.burst_bandwidth, desc.observed_bandwidth)
 
    print
    print 'Query took %0.2f seconds' % query.runtime
except Exception as exc:
    print 'Unable to retrieve the server descriptors: %s' % exc
 
print "Change tor circuit to fresh nodes"
 
with Controller.from_port(port = 9051) as controller:
    controller.authenticate("password")
    controller.set_options({'StrictNodes':'1'})
    controller.set_options({'ExitNodes': str(', '.join(nodes))})
    controller.signal(Signal.NEWNYM)
    print 'Exit nodes are now: %s'%(controller.get_conf('ExitNodes'))

Si vous regardez le code en détail, vous remarquerez que je filtre les nœuds avec des paramètres supplémentaires:

  • l’uptime des nœuds doit être supérieur à 2 minutes, sinon on tombe très souvent sur des nœuds défectueux
  • la bande passante observée doit être supérieure à la bande passante annoncée,
  • la bande passante doit être au minimum de 100 KB/s

Après quelques tests, ces critères s’avèrent suffisant pour surfer sur le site visé, et le finter ! :-)