加密货币交易所十二:安全性和风险控制
「我们用了冷钱包和多签」说的是两件事,多签管谁能批准,冷热管私钥在不在联网机器上,两者正交。这篇拆这个矩阵、提现路径上四道闸各自拦的是哪一种攻击者,以及 API 签名为什么挡不住重放。
「我们的资金用冷钱包加多签保管」是交易所安全页面上的标准句式。它说了两件事,而这两件事经常被当成一件。
多签回答的是「一个人能不能单独把钱转走」,冷热回答的是「私钥有没有出现在联网的机器上」。 它们是正交的两个维度,组合起来是四个格子:
| 热(私钥在联网机器上) | 冷(私钥离线) | |
|---|---|---|
| 单签 | 自动提现网关。最快,也最脆 —— 打穿这台机器就等于拿到钱 | 单点故障:钥匙丢了钱就没了,钥匙被偷了钱也没了 |
| 多签 | 大额热钱包。挡得住单台机器沦陷,挡不住签名流程被整体接管 | 主储备的标准做法。慢,但要同时攻破多个地理位置的多个人 |
看清这张表之后,「用了多签」这句话就没什么信息量了 —— 得问是几选几、私钥存在哪、以及批准流程走在什么信道上。一个 2-of-3 多签,如果三把私钥都放在同一个机房的三台服务器上,它防的只是单台机器故障,不防入侵。
提现路径上的四道闸,各拦一种攻击者
提现是唯一一条能把钱真正拿走的路径,所以闸口都设在这里。有意思的是这几道闸不是同一道防线的加厚,而是各自针对一类不同的攻击者:
| 闸 | 拦住的是 | 为什么它拦不住别的 |
|---|---|---|
| 二次验证(2FA) | 只拿到密码的人 | 会话被劫持之后,攻击者已经在 2FA 之后了 |
| 提币地址白名单 | 已经完全控制账号的人 | 但他仍然只能提到你自己预留的地址 |
| 新增白名单地址的冷静期 | 连你邮箱一起控制了的人 | 给真正的主人留出发现和冻结的时间 |
| 改密码 / 改 2FA 后锁定提现 | 刚刚完成账号接管的人 | 对长期潜伏的攻击者无效 |
这四道里最有力的是白名单。 前后三道都在拦「攻击者能不能进来」,白名单换了个问题:进来了又怎样,钱只能打到你自己早就登记过的地址上。它把「账号被盗」这件事的后果从「资产归零」压成了「攻击者能看能交易但拿不走钱」—— 这是一次量级上的改变,而不是概率上的改善。
代价也很直接:真正的主人换地址时也要等冷静期。急用钱的那一天,这道闸拦的是你自己。
API 通道的防线在密钥的权限位上,不在认证强度上
签名怎么算、recvWindow 为什么把时钟变成依赖、限流的两把尺子分别量什么 —— 这些属于接入层,第四篇 已经带着能跑的请求走过一遍。从安全的角度这里只补一条它引出的结论:
API 通道天然绕开了网页那套认证。 密钥直接调接口,不经过密码,也不经过二次验证 —— 一个每秒下十次单的程序没法每次手抄验证码。所以网页登录上加再多因子,对 API 通道一点保护都没有。这条通道的防线只有三样:权限位、IP 白名单、有效期。
权限位里最要紧的那一格是提现。一个跑策略的密钥需要交易权限,不需要提现权限,两个开关分开的全部意义就在这里:
只能交易的密钥 损失上限 = 被人对倒着刷掉的手续费和滑点能提现的密钥 损失上限 = 余额密钥泄露是常态事件 —— 提交进公开仓库、装了带后门的依赖、扔在某台被入侵的机器上。安全设计要按「它一定会泄露」来做,问题只是泄露那天损失的上限是哪一个数。
限流也要算进这一层。它对外挡恶意流量,对内挡失控的客户端:一个循环写错的策略程序能在一秒里发出几千笔下单请求,对交易所是撮合压力,对这个用户是真金白银的成交。所以撤单请求通常享有更宽松的限额 —— 系统过载时,最不该被限住的恰恰是「让用户能退出去」的那条路径。
风控在全仓和逐仓下算的不是同一个数
风控引擎的循环很简单:标记价格更新 → 重算受影响账户的保证金余额和维持保证金 → 比较 → 触发或者继续监控。分岔只在第二步的作用域上:全仓要把整个账户的所有仓位合起来算一遍,逐仓可以按仓位并行算。
所以逐仓的风控计算天然可以水平扩展,全仓不行 —— 一个账户的所有仓位必须在同一个地方汇总。账户越复杂,这一次汇总越贵,而它要在每次标记价格更新时重跑。保证金模式的选择同时决定了交易所的机器账单,这一点在讨论「全仓对用户更友好」的时候通常被忽略。
这里要纠正原文里的一个公式。它写的是:
账户风险率 = 总保证金 ÷ (总持仓价值 × 维持保证金率)分子是「总保证金」,一个不随价格变动的常数。按这个公式,市场怎么涨跌风险率都不动,风控循环的第一步「标记价格更新」就白触发了。分子必须是保证金余额,也就是钱包余额加上未实现盈亏 —— 价格变动正是通过未实现盈亏这一项传进风险判定的。这个判定条件的准确写法在 第七篇 里,那里也解释了「保证金率」这个词为什么会有两种方向相反的定义。
能改风控参数的人,等于能强平任何人
前面几节讲的都是「怎么挡住外面的人」。但交易所最危险的那几个开关,本来就在里面人的手上。
维持保证金率的阶梯表、标记价格的成分交易所和权重、清算罚金的比例 —— 这些都是配置,不是代码。配置的变更通常比代码轻得多:不用发版、不用过 code review、有时候后台点两下就生效了。
问题在于这些配置的杀伤力和资金转出是一个量级的:
- 把某个交易对的维持保证金率从 0.4% 调到 40%,效果是当场强平这个交易对上绝大多数杠杆仓位。不需要任何漏洞,只需要那个页面的写权限。
- 往标记价格的成分源里加一个自己控制的「交易所」并给它足够权重,就等于能把标记价格拉到任意位置 —— 而标记价格决定所有人的未实现盈亏和强平判定。
所以配置变更必须走和大额转出同一级别的流程:双人复核、每次变更留下谁在什么时候改了什么、以及一个生效前可以撤销的窗口。这类风险在渗透测试里查不出来,因为它根本不是漏洞 —— 系统完全按设计运行,只是那个设计允许一个人做这件事。
这一条也解释了为什么正经交易所会把阶梯表和指数成分公开。公开不只是为了用户能自己算强平价,也是为了让任何一次悄悄的修改都能被外部发现。把参数暴露在外面,是用透明度替代一部分内部管控。
大额转出走的是一条带回路的流程
这张图里唯一不平凡的地方是那条回路:签名不够就退回去继续通知、继续等,而不是失败退出。这意味着一笔大额转出可以在「等待签名」的状态里停留很久,系统必须为这个中间状态设计超时、提醒和撤回 —— 而这三样恰好是流程类代码最容易漏掉的部分。
两条路径最后汇到同一个终点:审计日志。无论走没走多签,都要落一条不可修改的记录。 审计日志的价值不在事前拦截(它拦不住任何东西),在事后能把「谁在什么时候批准了什么」还原出来。它是安全体系里唯一一个不提高安全性、但没有它别的都说不清的组件。
这一层的代价
多签的门限是个两难。 门限调高,一个人休假或者失联就动不了钱,极端行情下需要紧急调拨时尤其致命;门限调低,串通两个人就够了。没有一个门限对两种风险同时最优,只能按金额分层 —— 不同的额度走不同的门限。
冷备份的验证本身就是风险最高的那一刻。 冷钱包的私钥或助记词通常做门限分片备份(比如 Shamir 那类方案,分成 5 份、任意 3 份能还原)。但备份要定期验证还能不能用,而验证的唯一办法是真的把分片凑起来还原一次 —— 那一刻私钥完整地出现在某个地方,恰恰是它一生中最脆弱的时刻。分片存得越分散、平时越安全,验证时要把人和材料聚到一起的动作就越大。这个两难没有干净的解,只能靠降低验证频率和在隔离环境里做来缓解。
冷热分离让大额提现必然慢。 热钱包只留必要流动性,超过阈值的提现就得等一次离线签名。「秒到账」在结构上对所有金额都成立是不可能的,宣称做到了的,只能是把热钱包的额度放得很大 —— 也就是把风险放大了。
限流对做市商是成本,于是最该限的那批人限得最松。 高频用户会要求提高限额,交易所为了流动性通常会答应。结果是限流对普通用户是硬约束,对能真正打垮系统的那批账户反而宽松。这是流动性和稳定性之间的直接交换,没有技术解。
风控的严格程度没有最优点。 收紧了,正常用户在波动时被误伤、被提前平仓;放松了,穿仓的窟窿落到保险基金和其他用户头上。这两类代价落在不同的人身上,所以它不是一个可以纯技术优化的参数,是一个需要写进规则并且公开的决定。
这篇是 加密货币交易所的最后一篇,前一篇是 加密货币交易所十一:持仓管理。