一句话版本
系统总会出错:数据源连不上、校验程序崩溃、配置文件读不出来。这时只有两种选择:放行(fail-open,保可用性)或拒绝(fail-closed,保安全性)。 对于和钱、权限、身份有关的决策,默认应该 fail-closed:检查不了,就当没通过。难点在于把"出错"和"正常的空结果"区分开,并且让 fail-closed 不至于把系统变得一碰就停。
全文分三层:
- 通俗理解:门禁停电时,门该开还是该锁
- 实际用处:防火墙、杀毒软件、熔断器、名单更新
- 原理与工程要点:识别"出错"、数据源异常的保护、自动止损、可用性的代价
第一层:通俗理解
1.1 门禁停电了
一栋楼的电子门禁停电了,门应该:
- 自动打开(fail-open / fail-safe):着火时人能逃出去。消防规范通常要求逃生通道这样设计。
- 保持锁住(fail-closed / fail-secure):金库、机房、监狱,停电了也不能让人随便进出。
没有哪种绝对正确,要看出错时最怕的是什么:怕人出不去,还是怕坏人进来。
1.2 软件里的同一个问题
def is_allowed(address):
try:
return address not in fetch_blocklist()
except NetworkError:
return True # ← fail-open:名单拉不到,就当没问题
这段代码看起来"更友好":网络抖动时用户仍能继续操作。但攻击者只要想办法让名单服务拉不到(比如攻击它、或者在网络上干扰它),所有检查就都失效了。
改成 fail-closed:
except NetworkError:
return False # 检查不了,就当没通过
第二层:实际用处
| 场景 | 通常的选择 | 原因 |
|---|---|---|
| 防火墙规则 | 默认拒绝,只放行白名单 | 漏一条规则就是一个洞 |
| 金融交易风控服务超时 | 拒绝或转人工 | 放过一笔欺诈交易的代价远大于拒绝一笔正常交易 |
| 证书吊销检查(OCSP)失败 | 浏览器通常 fail-open("软失败") | 否则 CA 服务器一抖动,全网 HTTPS 都打不开。这也是证书吊销机制长期被诟病"形同虚设"的原因 |
| 电路熔断器 | 过载时断开 | 宁可停电,不能起火 |
| 软件熔断器(circuit breaker) | 下游故障时快速失败 | 防止故障层层蔓延,拖垮整个系统 |
OCSP 的例子说明:fail-closed 不是免费的,它把可用性压力转嫁到了依赖的服务上。
第三层:原理与工程要点
3.1 先识别"出错"
最隐蔽的 fail-open 不是 except: return True,而是把出错误当成正常的空结果:
| 看起来像 | 实际上 |
|---|---|
| 名单里没有这个地址 | 名单文件是空的(下载失败写了个空文件) |
| 余额是 0 | 节点返回了错误,被解析成了 0 |
| 没有命中任何风控规则 | 规则配置没加载上 |
| 签名校验通过 | 校验函数因为异常被跳过了 |
所以:区分"成功地得到一个空结果"和"没能得到结果",后者必须显式报错。
3.2 数据源更新的保护
外部名单(诈骗地址、制裁名单、恶意域名)需要定期更新。更新本身也可能被攻击:上游仓库被投毒,推送一份几乎是空的名单,下游自动接受后,所有检查形同虚设。
保护措施:
- 新名单比旧名单大幅缩水时拒绝替换,保留旧副本并告警;
- 拉取失败时继续使用上一份本地副本,并在界面上提示"名单已有多久没更新";
- 名单为空时视为错误,而不是"没有坏人"。
3.3 不一致时停下
在多源交叉验证里,两个独立来源给出不同答案时,正确的反应是停下,而不是挑一个看起来顺眼的。这是 fail-closed 在冗余系统里的应用。
3.4 自动止损
更激进的 fail-closed 是:发现系统可能已被攻破时,主动让它停止服务。比如发现前端页面和预期不符,就自动下线页面,宁可所有人暂时用不了,也不让任何人在可能被篡改的页面上签名(见《Bybit事件与前端完整性》)。
设计要点:
- 执行下线的凭证只有"下线"这一个权限(见《最小权限与密钥管理》),被盗用的最坏结果是服务中断,而不是数据被篡改;
- 下线后必须由人确认才能恢复;
- 要防止误报导致频繁下线,比如网络抖动不能触发,内容不符才触发。
3.5 代价与平衡
fail-closed 会让系统更"脆":任何一个依赖抖一下,用户就用不了。平衡的办法:
- 区分决策的风险等级:只读展示可以降级显示,动钱的操作必须 fail-closed;
- 给依赖做冗余:两个独立数据源都挂的概率远小于一个;
- 失败要说清楚:告诉用户"因为某某检查暂时不可用,为了安全暂停了这项操作",而不是一个模糊的报错;
- 监控失败率:fail-closed 的拒绝次数突然上升,本身就是一个值得调查的信号。
总结
| 层次 | 要点 |
|---|---|
| 直觉 | 出错时最怕什么?怕坏人进来,就锁门 |
| 原则 | 安全决策:检查不了就当没通过 |
| 陷阱 | 把"出错"误当成"空结果";名单被投毒缩水 |
| 平衡 | 按风险分级;依赖做冗余;失败说清楚;监控拒绝率 |