一句话版本
怎么确认"跟我说话的就是上次那个人"?最朴素的办法叫 TOFU(Trust On First Use):第一次见面时记下对方的公钥,以后每次都和记下的比对,一旦变了就报警。
它不能防"第一次就被骗",但能把攻击窗口压缩到第一次那一刻。SSH 的 known_hosts、Signal 的安全码、手机 App 的证书钉扎,都是这个思路;更进一步的"密钥透明"则试图连第一次也保护起来。
全文分三层:
- 通俗理解:记住对方的长相
- 实际用处:SSH、Signal、App 证书钉扎、HPKP 的失败教训
- 原理与工程要点:钉什么、存在哪、变了怎么办,以及"服务器不可信"时的用法
第一层:通俗理解
1.1 记住对方的长相
你第一次去一家小店,记住了老板的长相。以后每次去,你都会下意识地看一眼:还是那个人。如果某天柜台后面突然换了一个陌生人,却声称自己就是老板,你会立刻起疑。
TOFU 就是这样:
- 第一次连接时,记下对方的公钥(或公钥指纹);
- 以后每次连接都和记下的比对;
- 不一致就停下来,大声警告。
1.2 它防什么,不防什么
| 情况 | TOFU 的表现 |
|---|---|
| 攻击者第二次及以后才出现(中间人、服务器被换) | 能发现 |
| 攻击者从第一次就在中间 | 发现不了:你记住的就是攻击者 |
| 对方合法地换了密钥(重装系统、换手机) | 会误报,需要人去确认 |
所以 TOFU 的价值在于:攻击者必须从一开始就埋伏好,而这比"任何时候插一脚"难得多。
第二层:实际用处
- SSH:第一次连服务器时提示 "The authenticity of host ... can't be established",你回答 yes 后指纹写入
~/.ssh/known_hosts。之后指纹变了会出现醒目的REMOTE HOST IDENTIFICATION HAS CHANGED!,并拒绝连接。 - Signal / WhatsApp 安全码:对方的身份密钥变了(比如换了手机),聊天里会提示"安全码已更改"。想要连第一次也确认,就当面扫码核对(见《带外核对》)。
- 手机 App 证书钉扎:银行 App 只接受自己服务器的特定证书或公钥,即使手机上被装了恶意根证书,也无法中间人。
- HPKP 的失败:浏览器曾经支持网站通过 HTTP 头声明"以后只认这几个公钥"(HPKP,2015)。结果很多网站配错、丢了备用钥匙,把自己的用户锁在门外;攻击者还能反过来钉一个恶意公钥("勒索式钉扎")。2018 年前后主流浏览器陆续放弃了它,转向证书透明度(Certificate Transparency)。
- 密钥透明(key transparency):WhatsApp、iMessage 近年推出的机制,把所有用户的公钥写进一个公开可审计的日志,客户端可以检查服务器有没有给某人"偷偷换钥匙"。原理和哈希链审计日志相通。
Caloogy Wallet 也采用了公钥钉扎的思路。
第三层:原理与工程要点
3.1 钉什么
| 选择 | 优点 | 缺点 |
|---|---|---|
| 钉具体证书 | 最严格 | 证书一续期就失效 |
| 钉公钥(SPKI 指纹) | 续期时可以复用同一把密钥 | 换钥匙要提前规划 |
| 钉 CA | 灵活 | 保护弱:这家 CA 签的任何证书都会被接受 |
工程上一般钉公钥,并且同时钉一把备用公钥(离线保存),防止主钥匙丢失后把自己锁死。这正是 HPKP 失败的主要教训。
3.2 "服务器不可信"时的用法
TOFU 还有一个不太为人所知的用途:防自己的服务器被攻破。
设想用户在 App 里存了一份重要数据(比如一份共享的设置或名单),数据存在服务器上。如果服务器被攻破,攻击者可以悄悄改清单。对策:
- 清单的每一条都由用户设备上的私钥签名;
- 客户端第一次拿到用户公钥时,钉在本地;
- 以后每次读取清单,都用本地钉住的公钥验签;服务器给的公钥和本地不一致,就报警。
这样攻击者要么改不了清单(没有私钥),要么连公钥一起换(会被本地钉扎发现)。
3.3 变了之后怎么办
- 绝不自动接受新钥匙。自动接受等于没有钉扎。
- 给出清晰的提示:哪个值变了、什么时候、可能的合法原因(重装、换设备)和非法原因(中间人);
- 提供一条带外确认路径(打电话、当面扫码),确认后再更新钉扎记录。
3.4 新设备的问题
换了新设备,本地没有任何记录,只能再"第一次相信"一次服务器。这个空窗期需要别的机制兜底,比如新设备上线后先进入一段只读期(见《冷静期》),并通知老设备。
总结
| 层次 | 要点 |
|---|---|
| 直觉 | 记住第一次见到的公钥,变了就报警 |
| 应用 | SSH known_hosts、Signal 安全码、App 证书钉扎、密钥透明 |
| 教训 | HPKP 因配置失误和锁死风险被废弃;要有备用钥匙 |
| 进阶 | 用本地钉扎发现"自己的服务器"被篡改;新设备空窗期要兜底 |