Tor Deep Dive Part 2: How Tor Works and What You Are Trusting

Part 1 covered what happened to the exit relays. To judge what it means, you need the model underneath it: how a Tor circuit is built, who decides which relays exist, and what each party in the path can see. This part is that model, with real numbers pulled from the consensus on my Debian 13 test VM.

The path

A Tor client builds a circuit through three relays and sends your traffic through it, wrapped in layers of encryption, one layer per relay.

you  -->  guard  -->  middle  -->  exit  -->  destination
  • Guard (entry). The only relay that sees your real IP address. It does not know where the traffic is going.
  • Middle. Sees the guard on one side and the exit on the other. It knows neither who you are nor where you are going.
  • Exit. Removes the last layer and talks to the destination. It knows the destination and sees the traffic itself, unless the protocol on top is encrypted. It does not know who you are.

The split is the whole design. No single relay should see both who you are and what you are doing. That sentence is also the limit of the protection, and the rest of this post is about what falls outside it.

WhoSees your IPSees the destinationSees the content
GuardYesNoNo
MiddleNoNoNo
ExitNoYesYes, if unencrypted
The destination serverOnly the exit’sYesYes
Your ISPYesOnly that you use TorNo

Where the relay list comes from

A client needs to know which relays exist and what each one is allowed to do. That list is the consensus, a signed document produced by the directory authorities, a fixed group of relays run by people the Tor Project trusts. Each authority publishes a vote on the state of the network, and they combine the votes into one document.

My VM caches the current one. These are the real values from it:

sudo grep -E '^(network-status-version|vote-status|consensus-method|valid-after|fresh-until|valid-until|known-flags) ' /var/lib/tor/cached-microdesc-consensus
network-status-version 3 microdesc
vote-status consensus
consensus-method 36
valid-after 2026-10-21 11:00:00
fresh-until 2026-10-21 12:00:00
valid-until 2026-10-21 14:00:00
known-flags Authority BadExit Exit Fast Guard HSDir MiddleOnly NoEdConsensus Running Stable StaleDesc Sybil V2Dir Valid

A new consensus appears every hour, and this one stays valid for three hours after its valid-after time. Counting the authorities that voted and the signatures on the document:

sudo grep -c '^dir-source ' /var/lib/tor/cached-microdesc-consensus
sudo grep -c '^directory-signature ' /var/lib/tor/cached-microdesc-consensus
sudo grep '^dir-source ' /var/lib/tor/cached-microdesc-consensus | awk '{print $2}' | tr '\n' ' '
9
9
dannenberg longclaw bastet tor26 maatuska faravahar dizum gabelmoo moria1

Nine authorities, nine signatures. The relay count and the flags come from the status lines:

sudo grep -c '^r ' /var/lib/tor/cached-microdesc-consensus
sudo awk '/^s /{for(i=2;i<=NF;i++)c[$i]++} END{for(k in c)print c[k],k}' /var/lib/tor/cached-microdesc-consensus | sort -rn
FlagRelays in this consensus
All relays9,114
Running9,114
Fast8,739
V2Dir7,311
Stable7,124
Guard4,234
Exit2,701
HSDir2,397
BadExit47
MiddleOnly28

Of those, 1,163 relays carry both the Guard and Exit flags, which I come back to below.

Flags are the network’s immune system

Every relay claims what it is, and the authorities decide whether to believe it and what to allow it to do. Two flags matter for the incident:

  • BadExit tells clients not to use a relay as an exit. This is the long-standing flag for relays caught tampering with traffic.
  • MiddleOnly keeps a relay out of the entry and exit positions. Proposal 334 describes it as a flag set by the directory authorities, like BadExit, so the Network Health team can keep a relay out of high-risk positions while they investigate. I only have the proposal text for the rules, and it is marked as superseded by a later proposal, so treat the exact rules as approximate. The flag itself is in the live consensus above.

In Part 1 the directory authorities voted the BadExit and MiddleOnly flags onto the hijacked relays within hours of the Network Health team flagging them. The consensus on my VM, fetched on 21 October, carries 47 BadExit and 28 MiddleOnly relays. I cannot tell from the consensus which of those are the Part 1 relays.

The detail to keep in mind: authorities do not inspect relays. They see what a relay publishes and what the health team and the scanners report. A compromised relay with a clean-looking descriptor is as trusted as any other until someone notices. That is the gap the scanner in Part 7 pokes at.

Why guards exist

A client picks a small set of entry guards and keeps using them, instead of choosing a fresh entry relay for every circuit. The Tor man page gives the reasoning: constantly changing entry servers increases the odds that an adversary who owns some servers will observe a fraction of your paths. If you stay with an honest guard, an adversary who owns a bunch of relays rarely sits at the entry of your circuits. If your guard is hostile, you are stuck with it for a while. The Tor guard specification says clients choose “a small number of guard nodes”, gives three as an example, and does not state how long they are kept in the page I checked.

UseEntryGuards is on by default. The scanner in Part 7 turns it off in its private tor process on purpose, because it needs to choose the first hop itself. That is a good example of what not to do for normal use.

Circuits are also reused for a while and then replaced. The default MaxCircuitDirtiness is ten minutes: Tor will not attach a new stream to a circuit that was first used longer ago than that, though existing connections carry on.

Onion services

An onion service meets the client at a rendezvous point inside the network, and the traffic never goes through an exit. The service gets an address derived from its key, so you cannot be redirected to an impostor the way a hostile exit redirects ordinary traffic. Part 4 uses that property to make SSH over Tor safe, and it is the main reason I prefer an onion service to an exit for any service I run myself.

What you are trusting

Put the model together and the trust list is short but specific:

  1. The directory authorities, to produce an honest consensus. All nine sign it, so no one authority controls the list.
  2. Your guard, not to be a hostile one you are stuck with. It sees who you are, not what you do.
  3. The exit, with whatever you send unencrypted. It sees the destination and the plaintext, and as the Part 1 incident showed, it can rewrite what comes back.
  4. The software on the host. Every relay is a Linux box run by a volunteer. The Part 1 attackers did not break Tor. They broke hosts, then used the controls the host owner would have used.

The 1,163 relays with both Guard and Exit flags deserve a note. A client picks guards and exits separately, so a relay with both flags can end up in either position. That is normal and not suspicious. It does mean that one compromised relay is a candidate for more than one hop of your paths over time, and the safeguards in path selection, such as not using two relays from the same declared family in one circuit, depend on operators declaring their families honestly.

What Tor does and does not protect

Tor protects your network location from the sites you visit, and it protects the destination from your ISP. It does not:

  • Encrypt the traffic from the exit to the destination. That is the job of TLS or SSH on top.
  • Fix an application that leaks. A browser with the wrong settings, or a tool that does its own DNS lookups, can send identifying traffic outside the circuit. Part 9 measures one such leak.
  • Stop an adversary who can watch both ends of the circuit from correlating the timing.

A hostile exit therefore has a narrow but real power. It can read and rewrite anything unencrypted, and it can redirect connections that have no way for the client to verify the other end. SSH with a password and no pinned host key is exactly that case, which is why Part 5 builds the interception in a lab. Before that, Part 3 sets up the client on Debian 13 so there is something to test.