Tor

The Tor cheat sheet covers onion routing internals, torrc configuration, bridges and pluggable transports, onion (hidden) services, stem/nyx control, torsocks, DNS over Tor, and the OPSEC pitfalls that deanonymize users.

#How Tor Works

#Onion Routing in One Circuit

A Tor circuit is built through 3 relays. Each hop peels one layer of encryption, so no single relay knows both who you are and where you are going.

Hop Relay Knows Never Knows
1 Guard (entry) Your real IP Destination
2 Middle Prev + next relay Your IP or destination
3 Exit Destination + traffic Your real IP
You -> [Guard] -> [Middle] -> [Exit] -> Destination
      layer 3     layer 2     layer 1    plaintext*
* only if the destination itself is not encrypted (HTTPS/onion)

#Key Properties

  • Guard is sticky - reused for ~2-3 months to resist statistical profiling.
  • New circuit for each destination by default; rotated every ~10 min (MaxCircuitDirtiness).
  • Exit sees the same traffic your ISP would - use HTTPS or .onion end-to-end.
  • Onion (v3) services never leave the Tor network, so there is no exit node.
  • Directory authorities publish the signed consensus of all relays hourly.
  • Tor protects the transport layer, not the content you send.

#Install & Run

#Daemon Setup

# Debian/Ubuntu
sudo apt install tor torsocks nyx

# Arch / Gentoo
sudo pacman -S tor torsocks nyx
emerge net-vpn/tor

# Tor Browser Bundle (recommended for browsing)
# download from https://www.torproject.org/download/
# then verify the signature (see below)

# Start / enable the service
sudo systemctl enable --now tor
systemctl status tor

# Config lives here
/etc/tor/torrc          # system daemon
~/.tor/torrc            # per-user

#Verify the Download

# Import the Tor Browser signing key
gpg --auto-key-locate nodefault,wkd \
  --locate-keys [email protected]

# Verify the detached signature
gpg --verify tor-browser-*.tar.xz.asc \
  tor-browser-*.tar.xz
# expect: Good signature from "Tor Browser Developers"

#Health Check

# Confirm you are routed through Tor (JSON)
curl --socks5-hostname 127.0.0.1:9050 \
  https://check.torproject.org/api/ip
# {"IsTor":true,"IP":"185.220.x.x"}

# torsocks wraps any TCP app
torsocks curl https://check.torproject.org/api/ip

#torrc Essentials

#Core Directives

## /etc/tor/torrc - the common, documented options

# SOCKS proxy Tor listens on (apps point here)
SocksPort 9050
SocksPort 127.0.0.1:9150            # extra listener (Tor Browser uses 9150)

# Control port for stem / nyx (pick ONE auth method)
ControlPort 9051
CookieAuthentication 1              # file-based auth (preferred)
# HashedControlPassword 16:...      # from: tor --hash-password 'secret'

# Where Tor keeps state (keys, guards, consensus)
DataDirectory /var/lib/tor

# Logging
Log notice stdout
Log notice file /var/log/tor/notices.log

# Only used as a client (do NOT relay/exit)
ClientOnly 1

# Isolate streams so different apps do not share a circuit
SocksPort 9050 IsolateDestAddr IsolateDestPort

#Path Selection

# Restrict exit to specific countries (ISO 3166 codes)
ExitNodes {us},{de},{nl}
StrictNodes 1                       # never fall outside the set

# Force / avoid entry guards
EntryNodes {ch}
ExcludeNodes {ru},{cn},{ir},{by}    # avoid these countries entirely
ExcludeExitNodes {us}

# Pin a specific relay by fingerprint
ExitNodes ABCD1234...FINGERPRINT

# Longer-lived circuits (less churn, more linkability - trade-off)
MaxCircuitDirtiness 600

StrictNodes 1 makes the Exit/Entry sets hard constraints. Without it Tor treats them as a preference and may pick outside the set when no relay matches - which silently defeats geo-restrictions.

#Common Client Options

Directive Purpose
SocksPort 9050 SOCKS5 listener for apps
ControlPort 9051 stem/nyx control channel
ExitNodes {cc} Restrict exit country
ExcludeNodes {cc} Blacklist relays by country
StrictNodes 1 Enforce node sets strictly
MaxCircuitDirtiness Circuit reuse window (s)
NewCircuitPeriod How often new circuits are considered
EnforceDistinctSubnets 1 Avoid same /16 in one circuit
ClientOnly 1 Never act as a relay

#Bridges & Censorship

#Pluggable Transports

Bridges are unlisted relays; pluggable transports (PT) disguise the traffic so DPI cannot fingerprint Tor. Use them when Tor is blocked.

Transport Disguise Best For
obfs4 Random-looking bytes Most blocking; needs bridge address
snowflake WebRTC to volunteer proxies Fast-changing IPs, no fixed bridge
meek-azure Domain fronting (CDN) Heavy DPI where obfs4 IPs are blocked
webtunnel HTTPS website traffic Looks like normal web browsing

#Configuring obfs4

## torrc
UseBridges 1
ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy

# Get bridges: https://bridges.torproject.org
# or email [email protected] (from Gmail/Riseup)
Bridge obfs4 192.0.2.1:443 CERT=... iat-mode=0
Bridge obfs4 198.51.100.2:9443 CERT=... iat-mode=0

#Snowflake & meek

## Snowflake - no bridge line to distribute
UseBridges 1
ClientTransportPlugin snowflake exec /usr/bin/snowflake-client
Bridge snowflake 192.0.2.3:80 \
  fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 \
  url=https://snowflake-broker.torproject.net/ \
  ice=stun:stun.l.google.com:19302

## meek-azure - domain fronting via a CDN
ClientTransportPlugin meek_lite exec /usr/bin/obfs4proxy
Bridge meek_lite 192.0.2.4:443 \
  url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com

#Onion Services

#Host a v3 Onion Service

## /etc/tor/torrc - map an .onion address to a local port
HiddenServiceDir /var/lib/tor/my_service/
HiddenServicePort 80 127.0.0.1:8080
HiddenServicePort 22 127.0.0.1:22     # multiple ports allowed

# Reload, then read the generated 56-char v3 address
sudo systemctl reload tor
sudo cat /var/lib/tor/my_service/hostname
# abcd...xyz.onion   (base32, ed25519 pubkey)

# Files created in HiddenServiceDir:
#   hostname             -> your .onion address
#   hs_ed25519_secret_key -> PRIVATE key, back up + guard it
#   hs_ed25519_public_key

#Hardening an Onion Service

# Client authorization - only holders of a key can connect
HiddenServiceDir /var/lib/tor/my_service/
HiddenServiceVersion 3
# put authorized pubkeys in <HiddenServiceDir>/authorized_clients/
# <name>.auth: descriptor:x25519:<base32-pubkey>

# Defend against guard-discovery / DoS
HiddenServiceEnableIntroDoSDefense 1
HiddenServiceMaxStreams 100
HiddenServiceMaxStreamsCloseCircuit 1

# Single-hop (faster, LESS anonymous - server location leaks)
# HiddenServiceSingleHopMode 1        # only if server anonymity is not needed

Bind the backend to 127.0.0.1 only. If it also listens on a public interface, scanners can correlate the clearnet host with the onion service and deanonymize it. Strip server banners and version headers too.

#Vanity Addresses & Tools

# Generate a vanity .onion prefix (offline, brute-force ed25519)
mkp224o -d ./onions -n 1 myprefix
# then copy the produced keys into HiddenServiceDir

# Sanity-check reachability from a client
torsocks curl http://abcd...xyz.onion

#Control Port: stem & nyx

#nyx (live monitor)

# Terminal dashboard: bandwidth, circuits, connections, log
nyx
nyx -i 127.0.0.1:9051     # explicit control port

# In nyx:
#   c -> connections/circuits view
#   g -> circuit (graph) view
#   right/left arrows switch pages

#stem (Python control)

from stem import Signal
from stem.control import Controller

with Controller.from_port(port=9051) as c:
    c.authenticate()                 # cookie auth

    # Request a fresh identity (new circuits)
    c.signal(Signal.NEWNYM)

    # List active circuits and their path
    for circ in c.get_circuits():
        if circ.status == 'BUILT':
            path = ' -> '.join(
                f'{r.nickname}' for r in circ.path)
            print(circ.id, circ.purpose, path)

    # Read config live
    print(c.get_conf('SocksPort'))
    c.set_conf('ExitNodes', '{de}')

#New Circuit from the Shell

# Signal NEWNYM over the control port (cookie/none auth)
echo -e 'AUTHENTICATE ""\r\nSIGNAL NEWNYM\r\nQUIT' \
  | nc 127.0.0.1 9051

# Inspect circuits
echo -e 'AUTHENTICATE ""\r\nGETINFO circuit-status\r\nQUIT' \
  | nc 127.0.0.1 9051

#torsocks & DNS

#Wrapping Apps

# Route a single command's TCP through Tor
torsocks curl https://example.com
torsocks ssh user@host
torsocks git clone https://...

# Spawn a shell where everything is torified
torsocks --shell

# Point apps at the SOCKS proxy directly
export ALL_PROXY=socks5h://127.0.0.1:9050
curl --socks5-hostname 127.0.0.1:9050 https://example.com

#DNS over Tor (no leaks)

## torrc - resolve DNS through the Tor network
DNSPort 5353
AutomapHostsOnResolve 1
AutomapHostsSuffixes .onion,.exit

# Query it
dig -p 5353 @127.0.0.1 example.com
tor-resolve example.com 127.0.0.1:9050

Use socks5h:// (the trailing h) or --socks5-hostname so the resolver runs inside Tor. Plain socks5:// resolves DNS locally first - that leaks the hostname to your ISP even though the TCP goes through Tor.

#Tor Browser Security Levels

#Levels

Level JavaScript Notes
Standard Enabled everywhere Most usable, largest attack surface
Safer Disabled on non-HTTPS JIT off, some media click-to-play
Safest Disabled everywhere No JS/SVG/JIT, hardest to exploit
  • Raise to Safest for high-risk browsing - most drive-by deanonymization uses JS.
  • Never resize the window or maximize - letterboxing keeps the size bucketed against fingerprinting.
  • Do not install add-ons; each one makes you unique.
  • New Identity (Ctrl+Shift+U) wipes state and builds fresh circuits.

#Why Not Just a SOCKS Proxy?

Tor Browser is a hardened Firefox ESR that also normalizes your fingerprint (fonts, canvas, timezone, resolution, user-agent). Pointing a normal browser at 127.0.0.1:9050 anonymizes the IP but leaves you trivially fingerprintable and prone to WebRTC/DNS leaks.

#OPSEC Pitfalls

#Deanonymization

  • Do not torrent over Tor - clients leak your real IP in the peer handshake, ignoring the proxy.
  • Never log into a real identity (email, socials, bank). It links the pseudonym to you instantly.
  • Do not mix Tor and clearnet sessions for the same persona in the same browser.
  • Payments, addresses, phone numbers, and writing style all correlate across sessions.

#Traffic & Exit

  • Exit nodes can sniff and tamper with unencrypted traffic - always end-to-end encrypt (HTTPS/onion).
  • Malicious exits strip TLS (sslstrip) - watch for cert warnings; never click through them.
  • Onion services avoid exits entirely; prefer the .onion when one exists.
  • Tor does not hide that you use Tor from your ISP - use a bridge if that matters.

#Browser & App Hygiene

  • Set security level to Safest; JS is the #1 exploit and fingerprint vector.
  • Never open downloaded documents while online - PDFs/DOCX can fetch remote resources outside Tor.
  • Disable/avoid WebRTC, Flash, and any external handler that bypasses the proxy.
  • Keep Tor Browser updated; old versions carry known deanonymization CVEs.
  • Consider an amnesic OS (Tails) or isolation (Whonix) so leaks cannot bypass Tor at the network layer.

#See also

#Cyber Aurelien Guidi