一句话版本
2025 年 2 月,交易所 Bybit 的冷钱包被盗约 40 万 ETH(约 15 亿美元),是加密货币史上最大的单笔盗窃。区块链、智能合约、硬件钱包都没有被攻破,被攻破的是签名人看到的网页:攻击者篡改了多签钱包服务的前端代码,让签名人以为自己在做例行转账,实际签下的是"把钱包控制权交给攻击者"。 教训是:签名页面本身就是安全边界。它需要和服务器一样被保护、被监控、被验证。
全文分三层:
- 通俗理解:事情是怎么发生的
- 实际用处:同类事件,以及行业的反思
- 原理与工程要点:可复现构建、线上哈希巡检、部署版本监控
第一层:通俗理解
1.1 事件经过(根据公开调查报告整理)
美国联邦调查局(FBI)随后将此事归因于朝鲜的 Lazarus 黑客组织。
1.2 为什么这么多层防线都没拦住
| 防线 | 为什么失效 |
|---|---|
| 多签(需要多人签名) | 每个签名人看到的都是同一个被篡改的页面 |
| 硬件钱包 | 只显示了一串哈希或原始数据,人看不懂,只能"盲签" |
| 交易所内部流程 | 页面上显示的一切都"正常",没有触发任何人工复核 |
一句话:所有人都在信任同一块屏幕,而那块屏幕撒谎了。
第二层:实际用处
同类事件
| 事件 | 篡改的环节 |
|---|---|
| Badger DAO(2021) | 攻击者拿到 Cloudflare API 密钥,往前端注入脚本,诱导用户签下授权,损失约 1.2 亿美元 |
| Ledger Connect Kit(2023) | npm 包被投毒,大量前端自动加载(见《CSP与SRI》) |
| 多起 DNS 劫持 | 域名解析被改,用户访问到假前端 |
行业反思
- 前端托管必须和开发者电脑、业务服务器隔离,凭证最小化(见《最小权限与密钥管理》);
- 签名人要能独立于网页解码并核对交易(见《盲签与所见即所签》、带外核对);
- 前端需要像服务器一样被持续监控。
Caloogy Wallet 也采用了前端完整性巡检。
第三层:原理与工程要点
3.1 可复现构建(reproducible build)
同一份源代码,任何人在任何机器上构建,都应该得到逐字节相同的产物。这样就可以:
- 公开产物的哈希;
- 任何人自己从源码构建一遍,比对哈希,确认线上运行的就是这份源码。
实现要点:锁定所有依赖的版本和哈希、固定构建工具版本、去掉产物里的时间戳和随机路径。
3.2 线上哈希巡检
定时从外部抓取线上页面和所有脚本,计算哈希,和预期值比对:
| 要点 | 为什么 |
|---|---|
| 巡检程序放在一个和托管平台无关的机器上 | 托管平台被攻破时,巡检不会一起被攻破 |
| 预期哈希保存在巡检机器本地,不信任线上提供的清单 | 攻击者改页面时,顺手也能改线上清单 |
| 状态变化立即告警,平时定期发"心跳" | 没有心跳本身就说明巡检挂了 |
| 部署走统一脚本,部署时同步更新预期值 | 避免正常发布被当成攻击 |
巡检的局限是有空窗期:在两次巡检之间被篡改并被用户使用,只能事后发现。缩短间隔、配合下面的版本监控,可以把空窗期压得更小。
3.3 部署版本监控
很多托管平台(Cloudflare、Vercel、云存储)都提供 API,可以查询"当前线上是哪个版本"。监控程序定时查询,只要出现不在"我们自己部署过的版本"清单里的版本,立即告警。
这比比对页面内容更早:新版本一上传就能发现,不用等它真的被用户访问。
3.4 托管平台也会"好心办坏事"
CDN 和托管平台常有"自动优化"功能:插入统计脚本、压缩改写 HTML、混淆页面上的邮箱地址。对普通网站是好事,对签名页面却是"未经审查的代码注入"。应当:
- 关闭所有自动改写功能;
- 响应头加
Cache-Control: no-transform; - 用巡检验证页面确实没被改写。
3.5 自动止损
巡检只能通知人。如果发现异常时没人在线,用户仍可能在被篡改的页面上签名。更进一步的做法是:发现不符时自动下线页面(宁可停服,也不让人继续签名)。执行下线的凭证应当只有"下线"这一个权限,泄露了也改不了页面内容。
总结
| 层次 | 要点 |
|---|---|
| 事件 | 链和合约没被攻破,被攻破的是签名人看到的页面 |
| 根因 | 所有防线都信任同一块屏幕;硬件钱包只能盲签 |
| 防御 | 可复现构建、外部哈希巡检、部署版本监控、关闭平台改写、自动止损 |
| 底线 | 签名页面是安全边界,要和服务器同等对待 |