38.8 零信任

一句话

零信任把“网络位置”这个隐含凭证换成“每次请求的身份 + 设备 + 上下文校验”:它不消灭攻击,而是把“攻陷一台 = 攻陷一片”改成“每跳都要重新攻陷”,代价是每次决策的延迟和一个必须高可用的策略中心。

边界模型的三个假设,今天全部失效

假设 现实
内网是可信的 钓鱼、供应链、远程办公、云上多租户——攻击者常常已经在“内网”
边界是清晰的 云、SaaS、移动办公、合作伙伴接入,边界已经不存在
过了 VPN 就能访问 一台被控终端连上 VPN,等于拿到全网通行证

传统模型像城堡:护城河(防火墙)+ 吊桥(VPN),一旦进城,所有门都开着。零信任的前提是假设网络已经被攻破,所以每一次访问都要重新证明“你是谁、你的设备是否健康、你有权访问这个资源吗”。

数字:可达面降得不多,期望损失降得很多

设定 1000 台主机、20 个服务。从一台被控主机出发,3 跳内的网络可达主机数与“单跳真正攻陷”的概率:

架构 可达主机 单跳攻陷 3 跳期望攻陷
扁平内网 999 100% 999 台
按环境分段(3 段) 333 95% 333 台
按服务微隔离(白名单调用) 200 60% 59 台
零信任(逐请求身份 + 最小权限) 156 30% 8.4 台

皇冠资产占 1% 时,3 跳内摸到核心的概率:

扁平内网                        100.0%
按环境分段(3 段)                96.4%
按服务微隔离(白名单调用)          44.6%
零信任(逐请求身份 + 最小权限)      8.1%

这张表的关键不是“可达主机从 999 降到 156”(只降了 6 倍),而是每跳的攻陷概率从 100% 降到 30%——攻击是乘法的:

扁平内网:    1 × 1 × 1     = 必中
零信任:      0.3 × 0.3 × 0.3 ≈ 2.7%(每多一跳,衰减一个数量级)

🔑 零信任真正改变的是攻击的经济学:不是让攻击者到不了,而是让“每往前一步”都要重新付出漏洞利用/身份冒用的成本。

零信任不是产品,是三个“每次”

传统 零信任
信任依据:网络位置(IP/网段/VPN) 信任依据:身份 + 设备健康 + 上下文(时间、地点、行为)
授权粒度:一次登录,长期有效 授权粒度:每次请求、最小权限、短时效
默认姿态:内网默认放行 默认姿态:默认拒绝,显式允许
失败模式:边界破了就全通 失败模式:单点被控只影响它能证明有权访问的资源

三个“每次”:

  1. 每次连接都认证:工作负载用 mTLS + 短生命周期证书(SPIFFE/SPIRE),人用 SSO + MFA + 设备证书。
  2. 每次请求都授权:策略中心(PDP)按“主体 + 资源 + 动作 + 上下文”判定,而不是按“在不在内网”。
  3. 每个会话都可撤销:短 TTL、持续风险评估、异常即降权或断连(凭证有效期 60 分钟 → 5 分钟,暴露窗口从 60,000 缩到 5,000 凭证·分钟)。

NIST SP 800-207 的七条信条(意译)

  1. 所有数据源和计算服务都视为资源,不因位置而区别对待。
  2. 所有通信都要加密与认证,无论在内网还是外网。
  3. 访问按单次会话授权,会话结束即失效。
  4. 授权策略是动态的,依据身份、设备、行为、环境。
  5. 持续监控资产与设备的完整性。
  6. 所有认证与授权都严格执行,并在每次访问前重新评估。
  7. 尽可能收集状态信息(日志、流量、设备姿态),用于改进决策。

Google 的 BeyondCorp 是这套思想最早的大规模落地:员工不再“连 VPN 才能办公”,而是每个应用前面都有一个身份感知代理,按用户与设备状态放行。Forrester 的 Kindervag 更早提出了那句口号:“永不信任,始终验证”。

落地四阶段:每一阶段都有可量化的收益

阶段 动作 量化变化 常见阻力
① 清点 资产、身份、服务依赖、数据流 没有清单就没有策略;先画出“谁能访问谁” 没人维护 CMDB,依赖靠猜
② 强身份 SSO + MFA + 设备证书 MFA 覆盖 90% → 撞库成功降到 1/10(见 38.7 账号安全) 老旧系统不支持 SSO
③ 微隔离 从“网段”到“服务 + 身份”,mTLS 白名单 可达 999 → 200,期望攻陷 999 → 59 服务间调用关系不清,先观察后阻断
④ 持续验证 短 TTL、风险评分、最小权限、自动降权 凭证暴露 60,000 → 5,000 凭证·分钟 频繁重认证影响体验,需要 refresh 与无感续期

顺序不能反:没有身份与清单就上微隔离,会把正常调用也阻断;没有微隔离就做持续验证,只是在“全通”的网络上加了一道门禁。

代价:延迟、可用性、遗留系统

代价 数字 应对
每次请求多一次策略决策 缓存命中 95% 时平均 +0.25ms;全集中检查 +5ms 本地策略缓存 + 短 TTL 推送;把 PDP 调用放到连接建立阶段而非每请求
策略中心成为关键路径 PDP 可用性 99.9% → 每天约 864,000 次请求受影响 多副本 + 本地缓存 + 明确的降级策略
策略中心挂了的取舍 fail-closed:全拒(可用性归零);fail-open:全放(安全归零) 按资源分级:核心系统 fail-closed,低风险只读 fail-open + 告警
遗留系统不支持 mTLS/SSO 无法直接接入 身份感知代理(IAP)在网关终结 mTLS,后端仍用旧协议
运维复杂度 策略即代码、变更评审、灰度 策略要能“只观察不阻断”(dry-run)上线

⚠️ 零信任把可用性风险集中到了策略中心。做零信任之前,先回答:PDP 挂了,业务是“全拒”还是“全放”?这个答案必须写进架构决策记录,而不是等故障时现场决定。

零信任不解决什么

问题 为什么零信任管不了 该看哪
注入、XSS、越权 请求身份合法、路径合法,只是内容/对象有问题 38.6 常见攻击
合法账号的滥用(内鬼) 它确实有权限 行为基线、最小权限、审计、双人复核
终端被完全控制 攻击者用“合法身份 + 合法设备”活动 设备健康证明、EDR、行为检测
供应链投毒 依赖包本身带后门 38.10 供应链安全
数据本身没分级 不知道哪些是敏感数据,就无法定策略 38.5 数据分级与脱敏
业务逻辑漏洞 权限模型设计错误 38.3 授权 (RBAC / ABAC / 权限模型)

可运行的可达面与成本模型

package main

import (
	"fmt"
	"math"
)

// 每种架构下,从一台被控主机出发 3 跳内能“网络可达”的主机数,
// 以及每一跳真正攻陷(拿到身份/利用漏洞)的概率。
type model struct {
	name   string
	reach  int     // 3 跳内可触达的主机数
	perHop float64 // 单跳攻陷概率
}

// expected 返回 3 跳内的期望攻陷主机数(每跳分支数取 reach 的立方根)
func expected(m model) float64 {
	b := math.Cbrt(float64(m.reach))
	e := b*m.perHop + b*b*m.perHop*m.perHop + b*b*b*m.perHop*m.perHop*m.perHop
	if e > float64(m.reach) {
		e = float64(m.reach)
	}
	return e
}

func main() {
	models := []model{
		{"扁平内网", 999, 1.00},              // 网络通 = 直接拿下
		{"按环境分段(3 段)", 333, 0.95},         // 同段内仍然通,跨段要绕
		{"按服务微隔离(白名单调用)", 200, 0.60},     // 要利用服务间接口
		{"零信任(逐请求身份 + 最小权限)", 156, 0.30}, // 每跳都要有合法身份
	}

	fmt.Printf("%-32s %8s %8s %10s\n", "架构", "可达主机", "单跳攻陷", "3 跳期望攻陷")
	for _, m := range models {
		fmt.Printf("%-32s %8d %8.0f%% %9.1f 台\n", m.name, m.reach, m.perHop*100, expected(m))
	}

	// 皇冠资产占 1%:3 跳内摸到至少一台的概率 = 1 − e^(−期望攻陷数 × 1%)
	fmt.Println("\n皇冠资产占 1% 时,3 跳内摸到核心的概率:")
	for _, m := range models {
		p := 1 - math.Exp(-expected(m)*0.01)
		fmt.Printf("   %-32s %.1f%%\n", m.name, p*100)
	}

	// 策略检查的成本:缓存命中率决定平均延迟
	qps, pdpLatencyMs, cacheHit := 10000.0, 5.0, 0.95
	avg := pdpLatencyMs * (1 - cacheHit)
	fmt.Printf("\n策略检查:%.0f QPS,未命中 %.1fms,缓存命中率 %.0f%% → 平均 +%.2fms,PDP 负载 %.0f QPS\n",
		qps, pdpLatencyMs, cacheHit*100, avg, qps*(1-cacheHit))
	fmt.Printf("不做检查 +0ms;每次集中检查 +%.1fms(P99 还要再算排队);命中率降到 80%% → +%.1fms\n",
		pdpLatencyMs, pdpLatencyMs*0.2)

	// PDP 不可用时:fail-closed 全拒 vs fail-open 全放
	pdpAvail := 0.999
	fmt.Printf("PDP 可用性 %.1f%%:%.0f QPS × 86400s × 0.1%% → 每天约 %.0f 次请求受影响(fail-closed 全拒 / fail-open 全放)\n",
		pdpAvail*100, qps, qps*86400*(1-pdpAvail))

	// 凭证有效期 = 被偷后的暴露窗口
	stolenPerDay := 1000.0
	fmt.Println("\n凭证有效期决定暴露窗口:")
	for _, ttlMin := range []float64{60, 15, 5} {
		fmt.Printf("   TTL %2.0f 分钟:每天 %.0f 个凭证泄露 → 暴露 %.0f 凭证·分钟\n",
			ttlMin, stolenPerDay, stolenPerDay*ttlMin)
	}
}

输出:

架构                                   可达主机     单跳攻陷    3 跳期望攻陷
扁平内网                                  999      100%     999.0 台
按环境分段(3 段)                            333       95%     333.0 台
按服务微隔离(白名单调用)                         200       60%      59.0 台
零信任(逐请求身份 + 最小权限)                     156       30%       8.4 台

皇冠资产占 1% 时,3 跳内摸到核心的概率:
   扁平内网                             100.0%
   按环境分段(3 段)                       96.4%
   按服务微隔离(白名单调用)                    44.6%
   零信任(逐请求身份 + 最小权限)                8.1%

策略检查:10000 QPS,未命中 5.0ms,缓存命中率 95% → 平均 +0.25ms,PDP 负载 500 QPS
不做检查 +0ms;每次集中检查 +5.0ms(P99 还要再算排队);命中率降到 80% → +1.0ms
PDP 可用性 99.9%:10000 QPS × 86400s × 0.1% → 每天约 864000 次请求受影响(fail-closed 全拒 / fail-open 全放)

凭证有效期决定暴露窗口:
   TTL 60 分钟:每天 1000 个凭证泄露 → 暴露 60000 凭证·分钟
   TTL 15 分钟:每天 1000 个凭证泄露 → 暴露 15000 凭证·分钟
   TTL  5 分钟:每天 1000 个凭证泄露 → 暴露 5000 凭证·分钟

参数怎么给

你的阶段 优先做什么 别做什么
还没做 SSO/MFA 先把身份统一,MFA 覆盖到管理员与资金操作 直接上微隔离,会阻断正常调用
已有 SSO,内网扁平 画服务依赖图 → 观察模式微隔离 → 白名单放行 一次性全量阻断
已有微隔离 上 mTLS + 工作负载身份,缩短证书/令牌 TTL 只做网络 ACL,不做身份
多云/混合 统一策略中心 + 身份联邦 每个云一套独立策略
遗留系统多 身份感知代理(网关终结 mTLS/SSO) 要求每个老系统自己改造
已做零信任 持续验证 + 行为基线 + 自动降权 把策略写死,一年不更新

反模式

反模式 后果
买了 ZTNA/VPN 替代品就宣布“零信任” 只换了入口,内网仍然全通
只保护南北向,不保护东西向 一台被控主机仍可横向扫全网
策略中心单点部署 PDP 挂 = 全站不可用或全站放行
默认 fail-open 且无告警 策略失效时静默裸奔
微隔离一次性全量阻断 打断正常调用,被迫回滚,信任受损
策略写死、没有 dry-run 无法验证误杀,变更风险高
强制每请求都查中心 延迟 +5ms,P99 抖,团队最终关掉它
短 TTL 但没有 refresh/无感续期 用户频繁重登录,体验崩溃
只做认证不做授权 身份合法但权限过大,等于没做
把零信任当合规项目交差 文档齐全,攻击面没变

自检

Q1 零信任把 3 跳可达主机从 999 降到 156(约 6 倍),为什么期望攻陷数却从 999 降到 8.4(约 120 倍)?

▸ 答案:因为可达面只是“能不能发包”,攻陷是“能不能拿到身份/利用漏洞”,两者相乘。扁平内网每跳攻陷概率 100%(网络通即拿下),3 跳是 1×1×1;零信任每跳只有 30%(要有合法工作负载身份、要过策略、要利用目标接口),3 跳后每多一层就乘一次 0.3,且可达分支也被限制。降低可达面是线性收益,降低单跳成功率是指数收益,两者叠加才有 120 倍。

Q2 零信任和“上 VPN + 内网防火墙 + 堡垒机”的本质区别是什么?

▸ 答案:信任依据不同。VPN/防火墙信任“网络位置”:连上 VPN 就在内网,内网默认放行;堡垒机只是把运维入口收窄,登录后仍是全通。零信任信任“身份 + 设备 + 上下文”,并且每次请求都重新授权、默认拒绝、权限最小化、会话短时效。差别体现在横向移动:VPN 模型下一台终端被控即可扫全网(999 台),零信任下它只能访问策略允许的少数依赖(期望 8.4 台)。

Q3 策略中心(PDP)挂了,应该 fail-open 还是 fail-closed?怎么选?

▸ 答案:按资源分级,不能一刀切。核心写操作(支付、转账、权限变更)fail-closed——宁可拒,不可错;低风险只读(行情、文档、公开内容)可以 fail-open + 强告警 + 短时窗口,因为可用性损失更贵。同时必须做:PDP 多副本、本地策略缓存(TTL 内可离线判定)、降级开关与演练。关键是提前写进架构决策,而不是故障现场拍脑袋。

Q4 零信任能防住“越权访问”吗?为什么?

▸ 答案:不能。越权是“身份合法、请求合法,但访问了不属于自己的对象”(见 38.6 常见攻击),零信任的策略粒度通常到“服务/接口/资源类型”,判断不了“这条订单是不是你的”。它只能保证“调用方有权调用这个接口”,业务级的对象授权仍要在应用层做(WHERE owner_id = ?)。零信任缩小的是网络层爆炸半径,不是业务逻辑漏洞。

Q5 为什么令牌 TTL 不能无限短?从 60 分钟降到 5 分钟,收益和代价各是什么?

▸ 答案:收益是暴露窗口线性缩小:每天 1000 个凭证泄露时,60 分钟 TTL 对应 60,000 凭证·分钟,5 分钟对应 5,000(12 倍)。代价是刷新频率 ×12:refresh 请求量、PDP 负载、时钟/续期逻辑复杂度、以及用户可感知的重新认证(若 refresh 失败)。所以工程上用“短 access token + 长 refresh token + 无感续期 + 异常即撤销”,而不是把 TTL 压到 30 秒——那会把令牌系统变成每请求一次认证,延迟和可用性都不可接受。

跨篇相关:认证协议与令牌生命周期见 38.2 认证 (Session / JWT / OAuth2 / OIDC);授权模型与最小权限见 38.3 授权 (RBAC / ABAC / 权限模型);账号接管与 MFA 的量化收益见 38.7 账号安全;应用层漏洞(越权/注入)见 38.6 常见攻击;供应链攻击面见 38.10 供应链安全;数据分级决定策略粒度,见 38.5 数据分级与脱敏。