I noticed this bug ticket from Tor Project last week: Make exit flag depend on ports 80 and 443, not 6667 and it reminded me about a short talk I gave regarding how the port you connect to a service on, directly affects the anonymity you’re able to achieve. In short, visiting services on non-standard ports such as https://www.antitree.com:64201 increases the risk of you choosing a compromised circuit compared to visiting https://www.antitree.com:443.
Tor’s original threat model simply said that in order to compromise an individual, you would have to compromise 2 of the 3 nodes in a given circuit. Specifically the Guard and Exit. And since the nodes are run by anyone, there’s a risk that some of them could be compromised. So not factoring in anything else (path selection, guards, latency), your probability of choosing a compromised circuit comes out to be:
Where the probability of a compromise(
P) would be the number of malicious nodes
that are on the network(
M) divided by the total number of nodes available(
You can plug some numbers into that to think about it yourself:
N is 7000, and the NSA owned 1000 nodes (
there’s a 2% chance that the next circuit you used was compromised.
This is overly simplistic for the modern threat model because it doesn’t take into account that Guard nodes are relatively static, that the path selection algorithm specifically tries to reduce this probability, and that Exit node selection is based the port that you’re connecting to. The later is the point I’m trying to emphasize because it’s something users can control themselves.
As a reminder, when you tor client needs to visit https://www.antitree.com, it does a bunch of work before it starts deciding what path to select. It downloades the consensus from the Directory Authorities and it uses that to find an available Exit node that will allow you to connect to servcies on port 443.
If you were going to https://www.antitree.com:64201, it would have to find available Exit nodes that allow outbound on port 64201.
So the question is, how many services allow you to connect to ports 64201? The answer is: Not as many as you would like.
In the above image, you can see there’s just under 1000 exit nodes available on the network. They’re confirmed as functioning Exit nodes if they have ports 80 and 443 outbound on them. If you were to setup an Exit node yourself, and only allow outbound on ports 22, it wouldn’t register as an exit node on the network.
If you download the consensus doc and you crunch the numbers about available nodes that allow outbound on certain ports you’ll get something like this:
NOTE: Don’t forget that when you download the consensus document now, you are only getting a part of it which is why the total exits is so low compared to what the Tor Metrics website says
N is a factor of the port
you’re connecting outbound on, then visiting
https://www.antitree.com:64201 has a higher probability of compromise
compared to ports 443 or 80. This also means that connecting to ports
22 provides less anonymity than ports 443.
But more concerning are the services that could
induce you into visiting one of these malicious
services. For example, how hard would it be for me to include
into a web page?
What would tor do in that case? It would make a separate, less anonymous circuit just to connect and download that image.
Luckily, Tor Project’s tor network is big enough that this isn’t likely to change anything but if this concerns you, you could
You can download the latest consensus and crunch the numbers yourself:
import stem.descriptor.remote import matplotlib.pyplot as plt import pandas as pd data =  print("Downloading latest descriptors...") try: exits = [desc for desc in stem.descriptor.remote.get_server_descriptors().run() if desc.exit_policy.is_exiting_allowed()] for desc in exits: for rule in desc.exit_policy._get_rules(): if rule.is_accept: if rule.__str__() == "accept *:*": data += [int(p) for p in range(1,65536)] elif not rule.is_port_wildcard(): data += [p for p in range(int(rule.min_port),int(rule.max_port)+1)] except Exception as exc: print("Unable to retrieve the consensus: %s" % exc) randport = 64201 rpcount = data.count(randport) reduced_exitpolicy = [ 20, 21, 22, 23,53,79,80,81,100,143,389,194,443,636,587,993, 995,1194,1723,5900,6697,9999, ] print() print("Question: are there any ports not covered?") print("Total number of exits found in consensus: %s" % len(exits)) print("Total number of exits that can access port %s: %s" % (randport, rpcount)) for port in reduced_exitpolicy: print("Total number of exits that can access port %s: %s" % (port, data.count(port))) tor_ports = set(data) avail_ports = set(range(1,65536)) unused_ports = set(avail_ports - tor_ports) if len(unused_ports) == 0: print("Answer: All ports were covered") else: print("Ports missing:") _ = [print(x) for x in unused_ports]
A few of the slides from my talk: