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
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
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"]]]]
Analogy: Like nested Russian dolls - each relay peels off one layer.
Circuit Construction
# 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)
##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
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
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
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!)
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!
Implementation
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
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
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
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!
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
2. Privacy-Conscious Browsing
Use cases: - Avoid ISP surveillance - Prevent website tracking - Access blocked content - Research sensitive topics without records
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
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
Tor vs VPN
| Feature | Tor | VPN |
|---|---|---|
| Anonymity | High (multi-hop) | Low (single hop) |
| Speed | Slow (200-500ms) | Fast (~50ms) |
| Cost | Free | Paid |
| Trust | Distributed | Single provider |
| .onion support | Yes | No |
| Easy setup | Medium | Easy |
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:
- Explain layered encryption: "Each hop peels one layer, like an onion..."
- Node roles: "Entry knows source, Exit knows destination, but nobody knows both..."
- Hidden services: "Server also uses Tor via rendezvous point..."
- Trade-offs: "Slow (3+ hops) but anonymous..."
- Attacks: "Traffic correlation if adversary controls entry+exit..."
- Use cases: "Journalists, whistleblowers, privacy advocates..."
Related Concepts
- VPN — Alternative privacy solution
- End-to-End Encryption — Application-layer security
- Cryptography — Encryption fundamentals
- Anonymous Networks — I2P, Freenet
- Privacy Engineering — System design for privacy
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
OAuth 2.0 & OpenID Connect
Industry-standard protocols for authorization (delegated access) and authentication (identity verification), enabling secure "Sign in with Google/Facebook" and API access without sharing passwords.
Proxies: Forward vs Reverse
Understanding proxy servers that act as intermediaries between clients and servers, including forward proxies for client anonymity and reverse proxies for load balancing, security, and caching.
BitTorrent Protocol (P2P File Sharing)
Complete guide to peer-to-peer file sharing using BitTorrent protocol, covering torrent structure, piece exchange, tit-for-tat algorithm, DHT for decentralization, and real-world implementations powering massive file distribution networks.