Back to All Concepts
SecurityCryptographyNetworksExpert

Tor Network (Onion Routing)

Comprehensive guide to anonymous communication using onion routing, covering multi-layer encryption, circuit construction, hidden services, and real-world privacy protection mechanisms used by journalists, activists, and privacy-conscious users worldwide.

What is Tor?

Tor (The Onion Router) is a network that enables anonymous communication by routing traffic through multiple encrypted layers, making it extremely difficult to trace the origin or destination of internet traffic.

Key insight: No single node knows both who you are AND what you're doing.

The Privacy Problem

Normal Internet:
User -> ISP -> Server

Problems:
- ISP sees: "User at IP 1.2.3.4 visited example.com"
- Server sees: "Request from IP 1.2.3.4"
- Government can subpoena either
- Anyone in between can spy
Click to expand code...

Tor solution: Multiple encrypted hops, each knowing only the previous and next step.


How Tor Works

Architecture

User -> Entry/Guard -> Middle -> Exit -> Destination

Entry Node:  Knows your IP, doesn't know destination
Middle Node: Knows nothing (relays encrypted data)
Exit Node:   Knows destination, doesn't know your IP
Click to expand code...

Onion Encryption (Layered)

Original message: "GET example.com"

Encrypt 3 times:
Layer 3 (innermost): Encrypt("GET example.com", ExitKey)
Layer 2 (middle):    Encrypt(Layer3, MiddleKey)  
Layer 1 (outermost): Encrypt(Layer2, EntryKey)

Result: [Entry[Middle[Exit["GET example.com"]]]]
Click to expand code...

Analogy: Like nested Russian dolls - each relay peels off one layer.


Circuit Construction

python
# Simplified Tor circuit building
class TorCircuit:
    def __init__(self, tor_directory):
        self.directory = tor_directory
        self.nodes = []
        
    def build_circuit(self):
        """Build 3-hop circuit"""
        # 1. Select random nodes
        self.entry = self.select_node(role='guard')
        self.middle = self.select_node(role='middle', exclude=[self.entry])
        self.exit = self.select_node(role='exit', exclude=[self.entry, self.middle])
        
        # 2. Establish encrypted channel with entry
        entry_key = self.diffie_hellman(self.entry)
        
        # 3. Extend through entry to middle
        middle_request = self.encrypt(
            f"EXTEND {self.middle.ip}",
            entry_key
        )
        middle_key = self.send_and_decrypt(self.entry, middle_request, entry_key)
        
        # 4. Extend through middle to exit
        exit_request = self.encrypt(
            self.encrypt(f"EXTEND {self.exit.ip}", middle_key),
            entry_key
        )
        exit_key = self.send_and_decrypt(self.entry, exit_request, entry_key, middle_key)
        
        self.keys = {
            'entry': entry_key,
            'middle': middle_key,
            'exit': exit_key
        }
        
        return True
    
    def send_data(self, data):
        """Send data through circuit"""
        # Encrypt in layers (onion)
        layer3 = self.encrypt(data, self.keys['exit'])
        layer2 = self.encrypt(layer3, self.keys['middle'])  
        layer1 = self.encrypt(layer2, self.keys['entry'])
        
        # Send to entry node
        return self.send_to_node(self.entry, layer1)
    
    def select_node(self, role, exclude=[]):
        """Select random node from directory"""
        candidates = [
            n for n in self.directory.nodes 
            if n.role == role and n not in exclude
        ]
        return random.choice(candidates)
Click to expand code...

##Step-by-Step: Sending a Request

1. User wants to visit example.com

2. Build circuit:
   Select: Guard (Entry), Middle, Exit nodes
   Establish keys with each

3. Create onion packet:
   Data = "GET example.com HTTP/1.1"
   
   Packet = Encrypt_Entry(
     Encrypt_Middle(
       Encrypt_Exit(Data, ExitKey),
       MiddleKey
     ),
     EntryKey
   )

4. Send to Entry node:
   Entry receives packet
   Entry decrypts outer layer -> reveals next hop (Middle) + encrypted payload
   Entry forwards to Middle

5. Middle node:
   Middle decrypts its layer -> reveals next hop (Exit) + encrypted payload
   Middle forwards to Exit

6. Exit node:
   Exit decrypts final layer -> reveals "GET example.com"
   Exit makes request to example.com
   Exit receives response
   Exit encrypts response with ExitKey
   Exit sends back through circuit (reverse path)

7. Response travels back:
   Exit -> Middle -> Entry -> User
   Each layer encrypts on the way back
Click to expand code...

Node Types

1. Guard (Entry) Nodes

Purpose: First hop in circuit
Knows: Your real IP address
Doesn't know: Where you're going (encrypted)

Security: Use same guards for ~3 months
Why: Prevents timing attacks from malicious entry nodes
Click to expand code...

2. Middle (Relay) Nodes

Purpose: Obfuscation layer
Knows: Previous and next hop IPs
Doesn't know: Source, destination, or content

Role: Makes traffic analysis harder
Click to expand code...

3. Exit Nodes

Purpose: Final hop to destination
Knows: Destination (example.com)
Doesn't know: Your real IP

Risk: Can see unencrypted traffic (use HTTPS!)
Click to expand code...

Hidden Services (.onion Sites)

Problem: Tor hides the client, but server IP is still visible.

Solution: Both client and server use Tor!

Architecture

1. Hidden service publishes descriptor:
   Service -> Intro Points (3 nodes in Tor network)
   "I'm available at these introduction points"
   
2. Client wants to connect:
   Client creates Rendezvous Point (RP)
   Client -> Intro Point: "Tell service to meet me at RP"
   
3. Service connects to RP
   
4. Communication:
   Client <-> RP <-> Service
   Both sides use 3-hop circuits
   = 6 hops total!
Click to expand code...

Implementation

python
class HiddenService:
    def __init__(self, service_port):
        self.port = service_port
        self.onion_address = None
        self.intro_points = []
        
    def setup(self):
        """Setup hidden service"""
        # 1. Generate .onion address (hash of public key)
        private_key, public_key = generate_keypair()
        self.onion_address = hash_to_onion(public_key)
        
        # 2. Select 3 introduction points
        self.intro_points = [
            select_random_relay() for _ in range(3)
        ]
        
        # 3. Build circuits to intro points
        for intro in self.intro_points:
            circuit = build_circuit(destination=intro)
            send_introduce_message(circuit, public_key)
        
        # 4. Publish descriptor to DHT
        descriptor = {
            'onion_address': self.onion_address,
            'public_key': public_key,
            'intro_points': [ip.fingerprint for ip in self.intro_points],
            'timestamp': time.time(),
            'signature': sign(private_key, ...)
        }
        publish_to_dht(descriptor)
        
        return self.onion_address
    
    def handle_client_connection(self, rendezvous_point, cookie):
        """Connect to client via rendezvous point"""
        # Build circuit to rendezvous point
        circuit = build_circuit(destination=rendezvous_point)
        
        # Send rendezvous message
        send_message(circuit, {
            'type': 'RENDEZVOUS',
            'cookie': cookie
        })
        
        # Now connected via RP
        return circuit

# Client connects to hidden service
class TorClient:
    def connect_to_hidden_service(self, onion_address):
        """Connect to .onion site"""
        # 1. Fetch descriptor from DHT
        descriptor = fetch_from_dht(onion_address)
        
        # 2. Choose rendezvous point
        rendezvous_point = select_random_relay()
        rendezvous_circuit = build_circuit(destination=rendezvous_point)
        cookie = generate_random_cookie()
        
        # 3. Tell RP to wait
        send_establish_rendezvous(rendezvous_circuit, cookie)
        
        # 4. Contact service via intro point
        intro_point = random.choice(descriptor['intro_points'])
        intro_circuit = build_circuit(destination=intro_point)
        
        introduce_message = {
            'rendezvous_point': rendezvous_point.fingerprint,
            'cookie': cookie,
            'client_public_key': self.public_key
        }
        send_introduce(intro_circuit, introduce_message, descriptor['public_key'])
        
        # 5. Wait for service to connect to RP
        wait_for_rendezvous(rendezvous_circuit)
        
        # 6. Now connected!
        return rendezvous_circuit
Click to expand code...

Security Properties

What Tor Protects

Anonymity: Hides your IP from destination
Location privacy: Hides server location (.onion)
Censorship resistance: Difficult to block all Tor nodes
Traffic analysis resistance: Encrypted multiple times

What Tor Doesn't Protect

Application-layer data: Use HTTPS over Tor!
Timing attacks: Advanced adversaries correlating entry/exit
Malicious exit nodes: Can intercept unencrypted traffic
Browser fingerprinting: Use Tor Browser, not regular browser


Attacks & Defenses

1. Traffic Correlation Attack

Attacker controls entry AND exit nodes
Compares timing: "Traffic entered at 10:00:00.123"
                 "Traffic exited at 10:00:00.456"
-> Likely same circuit

Defense:
- Random path selection
- Long-lived guard nodes (harder to become your guard)
- Padding/timing randomization
Click to expand code...

2. Sybil Attack

Attacker runs many Tor nodes
Higher probability of being in your circuit

Defense:
- Directory authorities vet nodes
- Bandwidth weights (big legit nodes preferred)
- Guard node stability requirement
Click to expand code...

3. End-to-End Timing Attack

Global passive adversary watches all traffic
Correlates patterns even with encryption

Defense:
- Constant-rate traffic (padding)
- Mix networks (add delays)
- Not fully solved!
Click to expand code...

Real-World Use Cases

1. Journalism & Whistleblowing

Scenario: Journalist in authoritarian country

Without Tor:
Gov monitors: "Journalist IP contacted NYTimes"

With Tor:
Gov sees: "Journalist connected to Tor"
NYTimes sees: "Someone from Exit node contacted us"
Nobody knows: Journalist <-> NYTimes connection
Click to expand code...

2. Privacy-Conscious Browsing

Use cases:
- Avoid ISP surveillance
- Prevent website tracking
- Access blocked content
- Research sensitive topics without records
Click to expand code...

3. Corporate Security Researchers

Example: Security team investigating malware C&C server

Without Tor:
- Company IP exposed to criminals
- Malware authors know they're being watched

With Tor:
- Anonymous reconnaissance
- Safe intelligence gathering
Click to expand code...

Performance Considerations

Latency impact:
Regular: 20ms
Tor: 200-500ms (10-25x slower)

Why:
- 3 hops (Entry -> Middle -> Exit)
- Each hop adds latency
- Exit node might be far away
- Encryption/decryption overhead

Bandwidth:
- Limited by slowest node in circuit
- Volunteers donate bandwidth
- Heavy usage (torrenting) discouraged
Click to expand code...

Tor vs VPN

FeatureTorVPN
AnonymityHigh (multi-hop)Low (single hop)
SpeedSlow (200-500ms)Fast (~50ms)
CostFreePaid
TrustDistributedSingle provider
.onion supportYesNo
Easy setupMediumEasy

When to use VPN: Speed, streaming, simple privacy
When to use Tor: Anonymity, .onion sites, high-risk scenarios


Interview Tips 💡

When discussing Tor in system design interviews:

  1. Explain layered encryption: "Each hop peels one layer, like an onion..."
  2. Node roles: "Entry knows source, Exit knows destination, but nobody knows both..."
  3. Hidden services: "Server also uses Tor via rendezvous point..."
  4. Trade-offs: "Slow (3+ hops) but anonymous..."
  5. Attacks: "Traffic correlation if adversary controls entry+exit..."
  6. Use cases: "Journalists, whistleblowers, privacy advocates..."

Related Concepts

About ScaleWiki

ScaleWiki is an interactive educational platform dedicated to demystifying distributed systems, software architecture, and system design. Our mission is to provide high-quality, technically accurate resources for software engineers preparing for interviews or solving complex scaling challenges in production.

Read more about our Editorial Guidelines & Authorship.

Educational Disclaimer: The architectural patterns and system designs discussed in this article are based on common industry practices, technical whitepapers, and public engineering blogs. Actual implementations in enterprise environments may vary significantly based on specific product requirements, legacy constraints, and evolving technologies.

Related Articles