你搭建过的每一个网站,都建立在别人给的一层层许可之上。注册局把名字租给你。注册商可以随时把它关掉。证书颁发机构为你背书。解析器必须老老实实地回答查询,而端口必须在一个公开地址上敞开着,让任何人都能找到它、给它做指纹识别、然后攻击它。这里的每一环,都是一个可以被施压的对象,也是一处可以被攻击的表面。
洋葱服务把这一切全部拿掉。没有注册商,因为这个地址是从你自己生成的密钥推导而来的。没有 DNS,因为根本没有名字需要解析。没有证书颁发机构,因为这个地址本身就是公钥,连接会依据它进行自我验证。而且完全没有入站端口 — 你的服务器会主动拨出连接去迎接访客,所以它可以待在一个把所有流量都拦截丢弃的防火墙后面,却依然能被地球上任何一个角落连接到。
整个搭建过程大约十分钟,三行配置就够了。真正需要费心思的是机器上其余的部分,因为 Tor 背后的那个网页服务器根本不知道自己应该被隐藏起来,只要有人问对了问题,它就会兴高采烈地报出自己的主机名、自己的 IP,以及自己在明网上的那个孪生兄弟。这篇指南会先讲完那十分钟,然后讲清楚真正决定这件事有没有做成的那部分。
洋葱服务从这套体系里拿掉了什么
先从真正不同的地方讲起,因为这不只是一个长得奇怪的主机名那么简单。普通网站要依赖一整条由外部各方组成的链条,而洋葱服务根本没有这条链条。
- 没有注册商,也没有注册局。v3 洋葱地址是对一个 ed25519 公钥、外加一个校验和与一个版本字节做 base32 编码得到的结果 — 一共 56 个字符,在你自己的机器上不到一秒钟就能生成出来。没有人把它卖给你,所以也没有人能把它收回去,更没有什么续费日期。这和我们私密注册域名那篇指南里讲的风险模型,恰好完全相反。
- 没有 DNS。没有任何东西会去解析 .onion 名称。客户端是向 Tor 网络索取一份在那把密钥名下发布、并经过签名的描述符,所以没有解析器可被下毒,没有区域数据会泄露,也没有名称服务器可以被打垮。
- 没有证书颁发机构。地址本身就是公钥,所以客户端是拿自己输入的这个名字去核实这个服务的。这就是“自我认证”的含义:不存在为身份背书的第三方,因为身份和地址本来就是同一个东西。
- 没有入站端口。服务只会向少数几个介绍点发起出站线路,然后在那里等待。你的防火墙可以丢弃每一个入站数据包,网站照样能用。公开地址上没有任何东西在监听,所以没什么可扫描、没什么可提取指纹,也没什么能被直接泛洪攻击。
有一点历史遗留问题至今仍会造成困惑:旧版的十六字符地址已经成为历史。Tor 在 2021 年 10 月移除了对第二代洋葱服务的支持,那些地址如今在任何地方都已无法使用,凡是你找到的、还在描述它们的资料,都已经过时了。下面讲的全部都是 v3,这也是现在唯一还存在的版本。
同时也要说清楚,这一切都没能拿掉什么。数据中心里仍然有一台实体机器,有一家知道它存在的主机商,有一张有人付过钱的账单,还有一个可能被攻破的应用程序。洋葱服务只是把地址这一层挪到了别人够不着的地方,并没有把服务器本身挪走。
中继、网桥、出口、洋葱服务:四种不同的角色
人们接触 Tor,通常是想做四件很不一样的事,而这几件事的风险状况完全没法相提并论。搞清楚自己到底在做哪一件,很值得。
- 中间中继在其他中继之间转发加密流量。它承载的是别人的数据,却从不代替他们接触公开互联网,所以不会招来任何投诉。
- 网桥是给那些被封锁了 Tor 访问的人准备的一个未公开列出的入口。流量特征和中间中继一样,只是地址没有出现在公开目录里。
- 出口中继是别人的流量离开 Tor、以你的 IP 地址进入普通互联网的地方。招来滥用邮件和法律信函的正是这一种,需要专门认真对待 — 我们的Tor 中继指南对此有完整讲解。
- 洋葱服务是用来发布内容的。它只承载自己的流量,从不代表任何人去接触明网,因此完全不会招来针对你 IP 的滥用报告。它没有出口,也就没有什么可以从出口流出。
最后这一点值得多想一想,因为它经常被误解。运行洋葱服务,在操作层面上是你在 Tor 网络里能做的最安静的事。你的服务器发起的出站连接,看起来和普通的 Tor 客户端流量没有区别;它从不会作为连接源出现在别人的服务器上;而大多数人真正担心的那件事 — 别人的行为牵连到你的 IP 上 — 在结构上根本不可能发生。
运行洋葱服务也不需要你同时运行中继,而且这两件事最好分开来做。中继需要带宽、一个公开的 ORPort,以及一份公开的 ContactInfo。服务这些一样都不需要。如果你两个都想做,就分别放在两台服务器上。
泄密的几乎从来都不是 Tor
这一部分,官方文档写得很单薄,却恰恰是决定这件事到底有没有白做的关键。按照下文配置的 Tor,几乎不可能成为暴露你服务器的那个环节。真正会暴露你的,是它背后的那套软件,因为那套软件在编写时的默认假设,就是网页服务器理应被人找到。
下面是这些泄密渠道,大致按它们抓到人的常见程度排序:
- 公网 IP 上提供着相同的内容。如果你的网页服务器同时监听
0.0.0.0,任何扫描互联网的人都会在一个 IP 地址和一个洋葱地址上看到一模一样的页面。全网扫描器会持续给每一个开放端口建立索引,其结果还能按正文哈希和 favicon 哈希来搜索。一条查询就能把两者关联起来。这是洋葱服务被摘掉面具的头号常见方式,而起因往往只是一行配置错误。 - 服务器旗标与默认页面。一套原封不动装好的
nginx或apache2,遇到未知主机名会用默认页面来应答,会把自己的版本号打印在Server响应头里,还常常在报错信息里泄露机器的真实主机名。这里的每一项,都是一个可用于关联的把柄。 - 绝对 URL。重定向、canonical 标签、sitemap、RSS 订阅源、Open Graph 标签,还有密码重置邮件,全都喜欢吐出一个完整的明网 URL。洋葱版页面里只要藏着一个,就够了。
- 应用程序发起的出站请求。统计分析、网页字体、CDN 资源、头像服务、地图瓦片、webhook 以及更新检查,全都是从服务器的真实 IP 发出的,其中不少还会告诉第三方,那一刻正在渲染哪一个页面。一个自建站点,如果字体是从别人的域名加载的,就留下了一条自己并不想留下的审计记录。
- 邮件。这台机器发出的任何邮件,都会把它的真实 IP 盖在
Received头里。如果这个服务确实需要发邮件,那是一个需要专门认真解决的设计问题 — 可以从我们的邮件服务器指南开始,并且默认就该假设它什么邮件都不该发。 - 被重复使用的密钥和指纹。同一个 SSH 主机密钥如果同时在公网 IP 和洋葱地址上应答,就会把两者永久地关联在一起。同一张 TLS 证书、同一个 favicon、同一个统计标识符,或者两个项目共用同一个很有辨识度的错误页面,也都是一样的效果。
- 证书透明度。如果同一台机器曾经用 HTTPS 提供过某个明网域名,那个域名的证书就会被永久写进公开的、只能追加的日志里。这暴露的是这台机器,不是洋葱地址本身,但它确实暴露了这台机器。
以上所有情况的共同套路都是一样的:Tor 把地址藏好了,而机器上别的什么东西又把它公布了出去。下面的第八步会给出一份清单,帮你在别人之前先把这些漏洞找出来。
先想清楚:服务器的物理位置是不是秘密?
在安装任何东西之前,先回答一个问题,因为后面的一切都取决于它:这台服务器的物理位置,是不是你想要守住的秘密?
诚实的答案只有三种,它们会引向三种真正不同的搭建方式。
- 是的,位置就是重点。那么这台机器就只做一件事,别的什么都不做。没有明网站点,没有指向它 IP 的公开 DNS 记录,不发邮件,也没有其他任何服务在任何地方监听。付款方式也要与之匹配、不留下你的名字 — 我们的Monero 使用指南讲了具体做法,无需 KYC 的账户意味着除了一个收货地址,没有别的东西需要交出去。要保留完整的三跳配置。下文中每一个用匿名性换取速度的捷径,对你都是关闭的,而这没问题,因为你要优化的本来就不是速度。
- 不是,这台服务器本来就是公开的。你运营着一个普通网站,还想再给它配一个洋葱地址 — 也许是为了给身处审查之下的读者一条路,也许是因为有人不太愿意解析你的域名,又或者只是单纯想提供这个选项。反正没有什么需要隐藏的,所以你可以用单跳洋葱服务,把线路从六跳砍到三跳,再通过明网站点上的
Onion-Location响应头来公布这个地址。这是一个提升可达性的功能,拿它当理由完全站得住脚。 - 介于两者之间。大多数人其实都处在这种状态,而这恰恰是最危险的一种,因为“半藏着”根本不是服务器能拥有的一种属性。选一边站。如果位置真的要紧,就按它要紧的样子去搭建。如果不要紧,就别再为一个你根本没有在维持的匿名属性,去支付延迟上的代价。
把答案写下来,再继续往下走。这篇指南所讲的这类问题里,几乎每一个错误,都是按第一种情况搭建、却按第二种情况来运营造成的。
分步操作
- 选服务器,并决定这台机器上还能放什么
洋葱服务运行起来很便宜。Tor 本身在不承担中继任务时几乎不怎么占用 CPU,真正的负载来自你的站点本身原本就会产生的那部分开销,所以配置要按应用来定,而不是按 Tor 来定。一个静态站点或者一个小型自建应用,用 1 vCPU、2 GB 内存就很轻松;如果涉及数据库或某种语言运行时,就给它 2 vCPU、4 GB。放到我们的档位里,对应的是 Cub(1 vCPU、2 GB 内存、40 GB,$5/月)或者 Scout(2 vCPU、4 GB、70 GB,$9/月),两者都是全 NVMe,1 Gbps 流量不限量。
真正要紧的决定,不是选哪个档位。而是上一节里讲的那个决定:如果这台机器的位置本该保密,它就该只做一件事,而且仅此一件。上面不能有别的站点,任何地方都不能有指向它地址的 DNS 记录,不发邮件,也不能有别的任何东西在监听。在一台你已经在付费的机器上“再顺手加一样东西”的诱惑,正是关联性泄露最常见的产生方式。
付款方式也要与之匹配。这里的账户只需要一个用于接收凭据的邮箱地址,仅此而已,结账在链上完成 — Monero 使用指南和无卡购买指南会从零开始讲清楚整个流程。从一个最小化的 Debian 或 Ubuntu 镜像开始;下面的每一条命令都假定你在 Debian 12 或更新版本上,以 root 身份操作。
- 从 Tor 项目自己的软件源安装 Tor
要用 Tor 项目自己的软件源,而不是发行版自带的软件包。发行版打包的版本总是滞后,而这篇指南里用到的两个设置 — 介绍点限速器和工作量证明防御 — 只存在于比较新的版本里。
apt update && apt install -y apt-transport-https curl gpg lsb-release curl -s https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc \ | gpg --dearmor | tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null echo "deb [signed-by=/usr/share/keyrings/deb.torproject.org-keyring.gpg] \ https://deb.torproject.org/torproject.org $(lsb_release -cs) main" \ > /etc/apt/sources.list.d/tor.list apt update && apt install -y tor deb.torproject.org-keyring tor --versiondeb.torproject.org-keyring这个包会让签名密钥保持最新,所以这项工作只需要做一次,不会在一年后就失效。0.4.8 及以上的任何版本,都具备下文用到的全部选项。暂时先别配置任何东西。此时的 Tor 是作为客户端在运行,这已经足够满足后面验证步骤的需要。
- 把网页服务器绑定在回环接口,别的地方都不要绑
这一步是最常被跳过、也最常把整件事搞砸的一步。网页服务器必须只能从机器自身访问到,别的任何地方都不行。先安装 nginx,然后写一个只绑定回环接口的虚拟主机:
# /etc/nginx/sites-available/onion server { listen 127.0.0.1:8080; server_name _; server_tokens off; root /var/www/onion; index index.html; access_log off; }server_tokens off会把版本号从Server响应头和错误页面里去掉。把访问日志关掉是一个刻意的选择,而不是偷懒:每一个经由 Tor 而来的请求,看起来都来自127.0.0.1,所以日志里记不下任何关于访客的有用信息,却会留下一大堆更适合不留底的东西。接下来,再加一个在公网地址上应答、但什么都不肯说的兜底配置。nginx 的
444会直接关闭连接、不给任何响应,这是对扫描器能给出的最安静的答复:# /etc/nginx/sites-available/deny-direct server { listen 80 default_server; listen [::]:80 default_server; return 444; }把两者都启用,删掉自带的默认站点,然后确认一下实际在监听的都有什么:
rm -f /etc/nginx/sites-enabled/default ln -s /etc/nginx/sites-available/onion /etc/nginx/sites-enabled/ ln -s /etc/nginx/sites-available/deny-direct /etc/nginx/sites-enabled/ mkdir -p /var/www/onion && echo 'it works' > /var/www/onion/index.html nginx -t && systemctl reload nginx ss -ltnp仔细看
ss的输出。绑定在0.0.0.0或::上的条目,应该只有你打算保留的那一个 — SSH,如果你选择运行444兜底配置,那也算一个。其余的一切都应该只绑定在127.0.0.1上。如果你托管的应用自带监听器,也要检查一下它;不少框架默认就监听所有接口,而且完全不会主动提示这一点。 - 在 torrc 里声明这个洋葱服务
三行配置就能建出这个服务。把它们加进
/etc/tor/torrc,同时加上另外两条防御设置,这两条设置现在顺手开启,要比等出事了再开容易得多:# /etc/tor/torrc HiddenServiceDir /var/lib/tor/onion-site/ HiddenServicePort 80 127.0.0.1:8080 # rate-limit floods at the introduction points HiddenServiceEnableIntroDoSDefense 1 # make each connection attempt cost the client a little work HiddenServicePoWDefensesEnabled 1请仔细看这一行端口配置,因为这两个数字干的是不同的事。第一个是访客在洋葱 URL 里会用到的端口 — 把它固定在
80,这样就没人需要额外输入端口号。第二个是 Tor 在本地转发请求的目的地,也就是上一步里那个回环监听地址。这两个数字不需要一致,而且很多时候不一致反而更清楚。如果你想连回环 TCP 套接字也去掉,Tor 可以改用一个 unix 套接字来通信,这样一来,端口上就彻底没有任何东西在监听了。把服务指向一个套接字,同时把 nginx 也配置成监听它:
HiddenServicePort 80 unix:/run/onion-site.sock不要自己创建这个目录,也不要自己创建密钥文件。Tor 会在第一次启动时按它期望的属主和权限自动生成它们,而一个你手动创建、权限模式又不对的目录,正是这类服务悄无声息起不来的常见原因。
- 启动 Tor,读取你的地址
重启 Tor,看着它启动起来,然后拿到你的地址:
systemctl restart tor@default journalctl -u tor@default -n 20 --no-pager cat /var/lib/tor/onion-site/hostname这个文件里是 56 个 base32 字符,后面跟着
.onion,这就是你的地址 — 从 Tor 日志记录下它已经发布描述符的那一刻起就已经生效,通常一分钟以内就能完成。不需要注册什么,不需要传播什么,也不需要等待什么。再看看 Tor 还创建了什么,因为其中两个文件,比这台机器上其他任何东西都更重要:
ls -l /var/lib/tor/onion-site/你应该能看到
hostname、hs_ed25519_public_key和hs_ed25519_secret_key,它们所在的目录属主是debian-tor,权限模式是0700。这个密钥不是这个地址的一份凭证;它就是这个地址。把它复制到另一台机器上,那台机器就变成了你的服务。不做备份就删掉它,这个地址就再也没有任何人,包括你自己,能重新创建出来了。第九步会专门妥善处理这件事 — 不要跳过它。如果 hostname 文件没有出现,答案几乎总是在日志里:可能是一个 Tor 没法拥有的目录,一个它拒绝接受的权限模式,或者端口那一行写错了字。
- 访问它,并证明这就是你的机器
最直观的测试,是在 Tor Browser 里打开这个地址,这一步你也应该做。更有用的测试是在命令行里做,这样能看到响应头:
apt install -y torsocks torsocks curl -sI http://<your-address>.onion/ torsocks curl -s http://<your-address>.onion/ | head证明这确实是你的服务器。手动输入的地址是 56 个 base32 字符,只要挪错一个字符的位置,就可能连到别人的服务上去。在服务器上写一个随机令牌,然后通过 Tor 去请求那个确切的路径,再做比对:
head -c 16 /dev/urandom | base32 | tr -d '=' > /var/www/onion/token.txt cat /var/www/onion/token.txt torsocks curl -s http://<your-address>.onion/token.txt两个字符串完全一致,就说明你连到了自己的机器。测试完之后记得把这个文件删掉。
搞清楚这个测试证明了什么、又没能证明什么。从服务器本身运行这个测试,能证明这个服务已经发布并且可以访问,这正是你想知道的事。但它证明不了任何关于匿名性的东西,因为两端其实是同一台机器。要测试匿名性,得换一个完全不同的网络 — 如果 Tor 在你所在的地方被封锁了,网桥就是答案,我们那篇关于绕过 DPI 封锁的指南讲了这部分。
- 关掉这个服务不需要的端口
现在可以收获成果了。洋葱服务需要的入站端口是零,所以防火墙可以做到极致的简单粗暴:丢弃所有入站流量,只放行已建立的连接,以及你管理这台机器所需要的那些。我们的Debian 加固指南里有一套完整的 nftables 规则集;应用它,然后确认剩下的唯一入站例外就是 SSH。
接下来,可以考虑连这一个例外也去掉。SSH 是这台原本已经完全隐形的机器上,最后一个公开端口,而它其实也没必要公开。给它配一个属于自己的洋葱地址:
# /etc/tor/torrc HiddenServiceDir /var/lib/tor/onion-ssh/ HiddenServicePort 22 127.0.0.1:22重启 Tor,读出新的 hostname,然后通过本地的 Tor SOCKS 端口连接过去。在你笔记本电脑的
~/.ssh/config里加上这些:Host onionbox HostName <ssh-address>.onion User root ProxyCommand nc -X 5 -x 127.0.0.1:9050 %h %p确认这个方式能用之后,就在
sshd_config里设置ListenAddress 127.0.0.1,再删掉那条入站规则。这台机器现在在公开地址上彻底没有任何东西在监听了。两点提醒。在关上身后这扇门之前,要把洋葱线路彻底测试一遍,并且保留服务商提供的控制台访问方式,这样万一出错,代价只是重启一次,而不是丢掉整台服务器 — 我们这边的控制台就在控制面板里。另外,绝对不要让同一个 SSH 主机密钥同时在公网 IP 和洋葱地址上应答,因为两边的指纹会完全一样,而这恰恰是你最想避免制造出来的那种关联。
- 公布地址之前,先把漏洞找出来
在把地址告诉任何人之前,先走一遍这份清单。只需要五分钟,却是一个真正隐藏起来的服务和一个只是不太好找的服务之间的全部差别。
没有任何意料之外的东西在监听。每一行都应该是回环地址,或者是你自己刻意决定要开放的端口:
ss -ltnp公网 IP 上什么都提供不了。从别的地方,用两种协议直接问一遍这个 IP。你想要的是空的回复或者连接被拒绝;你的内容才是你不想看到的:
curl -sI --max-time 5 http://203.0.113.10/ curl -skI --max-time 5 https://203.0.113.10/你提供的内容里,不含任何明网 URL。通过 Tor 把页面拉下来,列出里面每一个绝对链接。凡是指向你自己控制的域名、某个 CDN、某个字体服务,或者某个统计端点的,都是一处泄漏或者一个可用于关联的把柄:
torsocks curl -s http://<your-address>.onion/ \ | grep -Eo 'https?://[^ "]+' | sort -u响应头和错误页面什么都不透露。检查一个真实存在的页面,再检查一个故意不存在的页面,留意版本字符串、主机名、文件路径和堆栈跟踪信息:
torsocks curl -sI http://<your-address>.onion/ torsocks curl -s http://<your-address>.onion/nothing-here | head -20应用程序没有在偷偷回家汇报。这一条不是一条命令,而是需要你通读一遍代码。关掉统计分析。字体、图标和脚本都自己托管。关掉头像和预览的自动抓取。去掉那些按计划定时对外呼叫的更新检查。把任何绝对 URL 相关的设置 — 站点 URL、canonical 主机、邮件发件人域名 — 都指向洋葱地址,而不是明网地址。
日常清理。用
timedatectl set-timezone UTC把机器设成 UTC 时区,这样时间戳就不会透露出它或者你可能身处何地。确认/server-status、/.git、备份文件和编辑器的交换文件都无法被访问到。而且,如果这台机器同时还提供着一个明网站点,回去把第三节再读一遍,因为上面这份清单,救不了一个从一开始就没有决定好定位的搭建方案。 - 备份密钥,因为密钥就是地址
再说一遍,因为这是那种没人能挽回的失误:私钥就是地址。没有注册商可以申诉,没有找回邮箱,也没有支持工单可提。丢了这些字节,这个名字就从互联网上永远消失了。
先停掉 Tor,让文件保持一致状态,再把整个服务目录打包,并在它离开这台机器之前先加密:
systemctl stop tor@default tar -C /var/lib/tor -czf - onion-site \ | gpg -c --cipher-algo AES256 -o onion-site-$(date +%F).tar.gz.gpg systemctl start tor@default把加密后的压缩包挪到一个不是这台服务器的地方 — 这么做的全部意义,就在于让它能在这台机器之外存活下来。我们的加密备份指南讲了怎么把这件事按计划自动执行,发送到另一个国家的一个只能追加的存储目标上,那才是它真正该待的地方。
恢复就是把这个过程反过来做一遍,而且权限设置不是可选项:如果权限模式不对,Tor 会拒绝启动,这在当下会让人有点恼火,却正是你想要的行为。
gpg -d onion-site-2026-09-14.tar.gz.gpg | tar -C /var/lib/tor -xzf - chown -R debian-tor:debian-tor /var/lib/tor/onion-site chmod 700 /var/lib/tor/onion-site chmod 600 /var/lib/tor/onion-site/hs_ed25519_secret_key systemctl restart tor@default cat /var/lib/tor/onion-site/hostname最后这一行就是检验标准。在另一台机器上得到同一个地址,就说明这份备份是真的。现在就先在一台用完即弃的服务器上演练一遍 — 一份没验证过的、不可替代的密钥备份,讲的其实是一个你曾经拥有过的密钥的故事。这里适用的道理,和我们迁移指南里讲的一样:你真正要买的,是能恢复这件事本身。
- 公布地址,并让人能够信任它
现在你手上有 56 个字符,没有人能读懂它、记住它,或者用肉眼验证它。这既是一个实实在在的可用性问题,也是一个安全问题,因为访客没法把你的地址和一个几乎一模一样的仿冒地址区分开来。分发方式是整个搭建工作的一部分,不是事后才想起来的补充。
把它公布在读者已经信任你的地方。如果你有一个明网站点,就把地址放进页脚,并按下一节的方法提供
Onion-Location响应头。否则,就用你的受众已经习惯和你联系在一起的任何渠道 — 一个已有的账号、一条签过名的消息、一张印出来的卡片。这个地址会自动继承你发布它的那个地方所拥有的可信度,仅此而已。如果风险值得,就给它签名。针对这个地址做一个签名、并且能用大家已经持有的密钥去验证,是唯一能让人在不必信任传递渠道本身的前提下,确认自己拿到的就是正确地址的办法。
不要依赖目录站或者搜索引擎。洋葱地址索引站确实存在,但并不完整,其中好几个还有过在热门地址旁边一并收录钓鱼仿冒版本的历史。能在那上面被找到是个额外的好处,但它不是一份分发方案。
用一个靓号前缀会有点帮助。
mkp224o这个工具会反复生成密钥,直到生成出一个以你选定的字符串开头的地址,这样一眼就能认出来:apt install -y gcc libc6-dev libsodium-dev make autoconf git clone https://github.com/cathugger/mkp224o && cd mkp224o ./autogen.sh && ./configure && make ./mkp224o -d ./keys -n 1 wolf这个代价会随着长度呈指数增长:几个字符只需要几秒钟,七八个字符在真实硬件上就要花上真正可观的时间,再长就基本不现实了。要老实地看待它能带来什么 — 攻击者同样可以去碰撞出一模一样的前缀,而前几个字符对上了就信任,恰恰是让人点错链接的那个习惯。把它当成一种品牌标识,而不是一种身份验证手段。生成出来的目录可以直接当作
HiddenServiceDir使用:按上一步的方法修好属主和权限,并且要在你信任的地方生成它,因为不管是谁在运行这个工具,谁就掌握着这把密钥。
在明网和洋葱地址上同时运行同一个站点
如果服务器的位置本来就不是秘密,两边一起运行既简单又确实有用 — 大型新闻机构、Debian,以及好几家搜索引擎,就是这么提供服务的。这里有两种机制和一笔交易,需要弄清楚。
用 Onion-Location 来公布它。在明网站点上加一个响应头,就能让 Tor Browser 在地址栏里显示一个“有 .onion 可用”的按钮,并提示切换。这个响应头只在通过 HTTPS 提供的页面上才会生效,而且取值必须是一个合法的洋葱 URL:
add_header Onion-Location "http://<your-address>.onion$request_uri" always;如果所在的主机没法设置响应头,也有一个等效的 HTML 写法,同样要求 HTTPS:
<meta http-equiv="onion-location" content="http://<your-address>.onion">另外也要把地址放在一个人能读到的地方,比如页脚或者关于页面。响应头只能触达已经在用 Tor Browser 的人;页脚则能触达其他所有人。
考虑使用单跳洋葱服务。一次普通的洋葱连接要经过六跳 — 客户端三跳,服务端三跳 — 而服务端这三跳存在的唯一理由,就是隐藏它的位置。如果这本来就不是秘密,你可以把它们去掉。延迟上的改善很明显,而且立刻就能感觉到。这个配置故意写得很直白:
# /etc/tor/torrc — these are INSTANCE-WIDE, not per service
HiddenServiceNonAnonymousMode 1
HiddenServiceSingleHopMode 1
SOCKSPort 0这两个选项必须一起设置,SOCKS 端口必须禁用,而且它们会作用于这个 Tor 实例上的每一个洋葱服务 — 没有针对单个服务的例外开关。如果你只设置了其中一个而没设置另一个,Tor 会拒绝启动,这其实是这套软件在帮你的忙。如果同一个实例上,既托管着一个位置公开的服务,又托管着一个位置不公开的服务,那就应该运行两个实例,或者更好的做法是,用两台服务器。
并且要接受这笔交易。同时公布两个地址,等于宣布同一个运营者同时运营着两者,而且从两边提供完全相同的内容,无论如何都会让它们变得极易关联。这就是这笔交易的内容,对一个本来就公开的站点来说,这不需要付出任何代价。但该讲究的卫生习惯还是要讲究:洋葱版本里不出现绝对的明网 URL,按主机分别限定 cookie 的作用域,让会话不会在两边之间流窜,再加上一份不会跨站拉取资源的内容安全策略。
客户端授权:当地址本身就是凭证
默认情况下,任何知道你地址的人都能连上这个服务。客户端授权是在网络层而不是应用层改变这一点,这个区别比听起来更重要。
启用授权之后,你的服务发布的描述符会针对一组客户端密钥加密。没有这些密钥中任何一把的访客,既没法解密它,也没法知道那些介绍点在哪里,因此完全连接不上 — 他们看不到登录页面,也得不到一个拒绝提示,什么都得不到。这个服务不是没有被列出来,而是彻底隐形。
给每个客户端生成一对 x25519 密钥。这里用到的所有工具,Debian 机器上都已经有了:
openssl genpkey -algorithm x25519 -out alice.prv.pem
# private key — goes to the client, never to the server
grep -v 'PRIVATE KEY' alice.prv.pem | base64 -d | tail -c 32 | base32 | tr -d '='
# public key — goes on the server
openssl pkey -in alice.prv.pem -pubout | grep -v 'PUBLIC KEY' \
| base64 -d | tail -c 32 | base32 | tr -d '='在服务器上,把公钥放进服务目录里的一个 authorized_clients 目录中。文件名随意,只要以 .auth 结尾即可,这样一来,撤销权限就只是删掉一个文件的事:
mkdir -p /var/lib/tor/onion-site/authorized_clients
echo "descriptor:x25519:<ALICE-PUBLIC-KEY>" \
> /var/lib/tor/onion-site/authorized_clients/alice.auth
chown -R debian-tor:debian-tor /var/lib/tor/onion-site
systemctl reload tor@default在客户端这边,私钥要放进 ClientOnionAuthDir 指定的目录里,文件以 .auth_private 结尾,里面还要重复写一遍地址。Tor Browser 在自己的数据目录下有专门存放这些文件的位置,遇到需要密钥的服务时,会自动提示你输入:
# /var/lib/tor/onion-auth/mysite.auth_private
<address-without-the-.onion>:descriptor:x25519:<ALICE-PRIVATE-KEY>对于一个预发布站点、一个管理面板、一处私密的文件收发点,或者一套只有你自己用的自建 Nextcloud 来说,这正是合适的工具 — 只要对“谁应该能看到这东西存在”这个问题,诚实的答案是“除了我们,谁都不该看到”,就适用。它的代价是真实存在的,而且是管理上的代价:你得通过一条自己信任的渠道,把密钥交到对方手里,还得记得事后把它们撤销掉。
让它稳定运行:拒绝服务、时钟与在线率
洋葱服务失效的方式很特别,没有一种看起来像是普通的服务中断。有四件事,值得在用到之前就先准备好。
拒绝服务才是真正的运维难题。因为没有 IP 地址可以过滤,常规防御手段都用不上,而蓄意发起一波介绍请求的泛洪攻击,成本却很低。Tor 提供了两个应对办法,都是按服务在 torrc 里设置的:HiddenServiceEnableIntroDoSDefense 1 会在介绍点上对请求限速,HiddenServicePoWDefensesEnabled 1 则会要求客户端解一道小小的工作量证明谜题,负载越高,谜题越难,这样一来,正常访客只需要多等一小会儿,而发动泛洪攻击的成本却会变得很高。从一开始就把两者都打开;在没有人攻击你的时候,这点开销可以忽略不计。
把时钟校准好。描述符的发布和查询,依据的是从一个共享网络数值推算出来的时间周期。一台时钟严重漂移的服务器,会把描述符发布到错误的地方,从而变得无法访问,而它自己的日志却显示一切正常。要确保 NTP 客户端在正常运行,让机器保持 UTC 时区,并且在某个服务莫名其妙无法解析时,第一时间检查一下时钟。
监控必须经过 Tor 才能进行。没有任何商业在线率监测服务能够探测 .onion 地址,也就是说,通常的解决办法在这里用不上。你需要自己从另一台没有托管这个服务的机器上做检测 — 用 cron 任务跑一条 torsocks curl -s --max-time 60,访问一个已知的 URL,再把返回内容和一个已知的字符串做比对,这样就够了,而且不花一分钱。
如果在线率很重要,就要规划不止一个实例。OnionBalance 能让好几台后端服务器共用同一个洋葱地址:一个前端实例持有拥有这个地址的密钥,并发布一份指向各后端介绍点的描述符,这样你就能实现负载分担和故障切换,而地址本身始终不变。这比大多数网站真正需要的复杂一些,但它是官方支持的解决方案,在你把自己逼到只剩一台机器可用之前,值得先知道它的存在。
最后一个运维习惯:重启 Tor 会让服务离线几秒钟,用来重新发布描述符;而一次抹掉 /var/lib/tor 的重装,则会让它永远离线。请把这个目录当成一把私钥来对待,因为它本来就是。
老实的局限
洋葱地址拿掉了注册商、解析器、证书颁发机构和开放端口。除此之外,它什么都没拿掉,而说清楚剩下的部分,正是区分有用的工具和虚假的安全感的关键所在。
它修不好应用程序本身。一个存在漏洞的网页应用,放在洋葱服务背后,依然是一个存在漏洞的网页应用;很多攻击者拿到代码执行权限之后,第一件事就是发起一次会暴露服务器真实地址的出站请求。按照我们Debian 加固指南里那十分钟的流程,把这台机器过一遍,及时打上补丁,并且哪怕没有任何端口在监听,也要把它当成暴露在外的机器来对待。
它不会把服务器瞒着运营它的人。我们知道一台服务器存在,知道它是什么时候部署的,也知道它的 IP 地址是什么,因为分配这一切的人正是我们自己。这一点对任何主机商、在任何地方都成立,凡是告诉你并非如此的人,都是在向你推销什么东西。我们能够精确说清楚的,是我们拿这些信息做了什么,这一点写在我们关于加密货币 VPS 到底隐藏了什么的老实说明里。
它单靠自己,挡不住一个有耐心、有资源的对手。流量的大小和时间规律,在网络边缘是看得见的,一个每逢某个特定的人睡觉就安静下来的服务,本身就说明了一些问题。如果内容本身能指认出你,它也帮不上忙:同样的写作风格、同样的 PGP 密钥、同样的头像,或者和某个公开身份相同的论坛 ID,不管传输层做得多好,都会把这个环闭合上。
说说我们自己这边,方便你参照校准:洋葱服务在我们任何一个档位上都受欢迎,也不需要我们做任何特殊处理,因为它们不开放端口,也不会产生任何滥用流量。我们运营的无需 KYC 的账户,只需要一个地址来投递凭据。日常的版权投诉,我们当作运营事务来处理,而不是自动下架;我们会在服务器所在的司法辖区,对有效的法院命令采取行动;而那条不可退让的底线绝对存在,在这里和其他任何地方一样适用:不许有 CSAM,不许有恐怖主义内容,没有例外。在你搭建一个你不愿意日后搬家的东西之前,先读一读可接受使用政策,了解我们是如何处理滥用报告的。我们的Tor 友好型 VPS 页面里有档位建议。
常见问题
运行 .onion 网站需要域名吗?
不需要,而这正是这件事的意义所在。地址是在你自己的服务器上,从一把密钥在一瞬间生成出来的,所以没有注册商可以向其购买,没有续费日期会错过,没有 WHOIS 记录,也没有谁有权把它吊销。你放弃的是易读性:.onion 地址是 56 个没人能记住或者打得出来的字符,只能在 Tor Browser 或者支持 Tor 的客户端里打开,而且搜索引擎大多也不会去收录它。正因如此,很多人两个都用 — 域名负责触达,洋葱地址负责保住连续性。我们那篇关于私密注册域名的指南,讲的正是这对组合的另外一半。
我需要开放端口或者配置端口转发吗?
都不需要。这正是洋葱服务和普通托管方式之间结构性的差别:这个服务是向 Tor 网络内部的介绍点发起出站连接,然后在那里等客户端上门,所以从来不会有入站连接抵达你的服务器。一个丢弃所有入站数据包的防火墙,对它完全没有影响。实际效果是,你完全可以得到一台在公开地址上什么都不监听的机器 — 连 SSH 也不例外,只要像第七步那样给它配一个属于自己的洋葱地址 — 这样的攻击面,比任何常规托管的站点所能做到的都要小得多。
.onion 地址需要 HTTPS 证书吗?
几乎肯定不需要。洋葱地址本身就是这个服务的公钥,所以连接早就针对访客输入的这个名字完成了端到端的加密和身份验证 — 证书颁发机构在这方面已经没有什么可以再添加的了,而且 Tor Browser 会把 .onion 源当作安全上下文来对待,所以需要 HTTPS 才能用的功能,不需要证书也能正常工作。有极少数 CA 愿意为 .onion 名称签发证书,如果你需要在地址栏里显示一个组织名称,或者同时混用洋葱资源和明网资源,这偶尔会有用。对一个普通站点来说,在回环监听器上用纯 HTTP,才是正确而且正常的配置。
为什么我的洋葱服务很慢?能让它变快吗?
标准的洋葱连接要经过六个中继,客户端选三个,你的服务再选三个,你感受到的延迟大部分都来自这段往返路程。有三件事能帮上忙。如果你服务器的位置不是秘密,单跳洋葱服务能把属于你这一侧的三跳直接砍到零,延迟大致减半 — 参见上文关于同时运行两者的那一节,并且要注意,这个设置是整个实例级别的,而且是明确放弃服务器位置隐私之后才能用的。第二,让站点本身变轻:不用外部资源,不从别的主机加载字体或脚本,加上激进的缓存策略,因为每多一次请求,就要再完整地承担一次线路的全部代价。第三,检查一下自己是不是正在被泛洪攻击;启用 HiddenServicePoWDefensesEnabled,能让服务在遭遇介绍点滥用时依然可用,否则这种情况看起来会和性能不佳一模一样。
我可以让同一个站点同时在明网和以洋葱服务的形式运行吗?
可以,而且对一个公开项目来说,这是个不错的主意 — 它能给身处审查之下的读者提供一条路径,而且不需要他们解析你的域名。不过要清楚这到底是什么:从两边提供完全相同的内容,会让它们变得极易关联,所以这是一个提升可达性的功能,而不是一个隐藏功能。在 HTTPS 站点上用 Onion-Location 响应头来公布这个地址,再把它放进页脚,留给其他所有人。如果服务器的位置真的应该保密,那就完全不要这么做;把洋葱服务放在一台什么别的都不做、没有任何 DNS 记录指向它、也不发任何邮件的机器上运行。
如果我丢了洋葱密钥会怎么样?
这个地址就永远消失了,对所有人都是如此。不存在什么找回流程,因为根本不存在什么权威机构 — 这个名字是从 /var/lib/tor/<service>/hs_ed25519_secret_key 里的那把密钥,通过数学方法推导出来的,没有这些字节,任何人都没法再以它的名义重新发布。这就是没有注册商能把它从你手里拿走所要付出的代价。把这个目录加密备份到服务器之外,在一台用完即弃的机器上恢复一次,确认地址真的能找回来,同时要记住,任何拿到这个文件的人,都会变成你的服务本身,所以它应该得到和任何其他私钥一样的对待。
我可以在 VPSCrypto 上托管洋葱服务吗?
可以,而且任何档位都可以,也不需要我们做任何特殊处理:洋葱服务不开放任何入站端口,也不会针对你的 IP 产生任何滥用流量,这让它成为你能运行的最安静的东西之一。账户无需 KYC,交付凭据只需要一个地址,结账在链上完成,Monero 是首选币种,服务器大约一分钟就能上线。滥用底线在这里和其他任何地方完全一样地适用 — 不许有 CSAM,不许有恐怖主义内容,没有例外 — 而且我们会在服务器所在的司法辖区,对有效的法院命令采取行动。档位方面的建议参见我们的 Tor 友好型 VPS 页面,其余部分参见可接受使用政策。

