后端信任边界与威胁建模:安全控制为什么必须落在数据流上
从资产、主体、信任边界、数据流、威胁场景和剩余风险建立可执行的后端安全模型。
OWASP Application Security Verification Standard 与 NIST SSDF 把安全要求落到可验证控制和开发生命周期;漏洞名称不能替代系统自己的资产与数据流。
安全从“谁相信了谁”开始
一次创建订单请求会穿过浏览器、反向代理、应用、缓存、数据库和消息系统。TLS 只证明某段传输通道,网关认证只证明网关看到的凭证;应用仍要判断调用主体、租户、对象、动作和当前业务状态。内部网络、消息头、反序列化对象和服务账号都不是天然可信输入。
先列资产及损害:账户接管、越权读、资金状态被改、秘密泄露、审计丢失、资源耗尽。再画主体和数据流,在跨进程、跨网络、跨租户、跨权限、跨存储与跨管理域处标边界。控制必须贴在实际流上,才能回答谁执行、失败默认值、旁路和观测证据。
威胁场景要写成可失败的因果链
“存在 SSRF 风险”不可测试;“普通租户可提交 webhook URL,下载器可解析任意地址,出站网络可访问元数据服务,响应会进入任务日志”才是场景。把前置条件、信任错误、可达资产、业务影响和当前控制写全,再按可能性、影响、暴露面和可检测性排序。
STRIDE 等分类适合防漏项,不负责决定优先级。高风险控制进入发布阻断,中风险进入有 owner 和期限的计划,接受风险必须记录假设与到期复审。架构变化、数据分类变化或依赖升级会使旧结论失效。
控制必须同时拥有预防、检测和恢复
对象级授权预防越权,授权拒绝事件和异常导出量帮助检测,撤销会话、冻结主体、回滚策略与重新对账负责恢复。只有 WAF 或扫描器无法覆盖业务状态;只有日志没有实时拒绝也无法保护资产。纵深防御不是重复堆产品,而是让不同失败模式不能同时穿透。
负向测试从跨租户对象、伪造转发头、旧权限缓存、重复请求、超大输入、依赖超时和审计写入失败入手。每个控制都要证明允许路径仍工作、拒绝路径无副作用、日志不泄密且恢复动作可执行。
数据流图要一直画到权威提交
只画“用户—服务—数据库”会隐藏网关补头、缓存授权、异步消息、批处理账号和管理后台。每条流标注协议、身份来源、数据分类、完整性证据、重放可能、资源预算和最终写入者;同一份数据被序列化、缓存、导出或写日志时会跨越新的信任边界。
控制表使用“场景—执行点—权威源—失败默认值—证据—恢复”六列。比如跨租户订单读取的执行点在查询条件,权威源是认证主体的 tenant,失败默认值是零结果或统一拒绝,证据是带策略版本的审计,恢复是冻结主体并回查访问记录。这样才能发现只在 Controller 做比较、异步导出却绕过检查的断链。
威胁模型也要承受架构变更
新增 CDN、把同步调用改成消息、开放伙伴 API、引入大模型工具或把文件解析迁到第三方,都会改变主体和数据驻留。变更评审应对旧图做差异:新增流、扩大权限、延长保留、降低可检测性或增加不可逆副作用。没有变化证据时不能沿用旧风险接受结论。
事故复盘反向更新滥用案例和自动测试。如果一次权限缓存陈旧导致越权,修复不止缩短 TTL,还要明确安全版本、失效确认、高风险权威读取和缓存故障默认值。威胁模型最终应表现为代码约束与监控,而不是静态图片。
用滥用案例穿透正常业务叙事
正常用例写“用户导出自己的账单”,滥用案例要继续追问:能否替换 accountId、能否一次导出十万条、能否通过异步任务在权限撤销后继续、下载地址是否会被转发、导出文件在对象存储保留多久。每个问题会落到不同执行点:对象授权、配额、任务重新授权、临时 URL 与生命周期清理。只在入口加一个角色判断无法覆盖这条链。
管理员、客服、批处理、迁移脚本和 break-glass 账号是最容易被图省略的主体。它们往往拥有跨租户或批量能力,认证强度、审批、时间限制、操作原因和会话录制应高于普通用户。应急账号不能长期在线,也不能与日常账号共享凭证;每次启用都触发独立告警和事后复核。
风险排序必须能推动具体发布决策
风险分值不是精确概率,而是让团队比较“远程未认证资金修改”和“需内部账号的低敏字段读取”。评分所用资产等级、互联网暴露、权限门槛、可批量性和检测能力要保存,避免只剩一个神秘数字。对能破坏核心不变量、跨租户或导致大规模秘密泄露的场景,即使复现成本高也应作为发布阻断。
接受风险不是永久忽略。记录补偿控制、监控查询、应急动作、owner、到期时间和触发重新评估的信号;当依赖变更、流量扩大或控制失效时自动重开。安全债务若没有到期与验证证据,会在组织记忆消失后变成未知旁路。
用一次订单导出完成模型验算
调用者先经认证得到主体与认证强度,导出服务按主体派生 tenant 和可见账户集合,数据库查询携带这些条件,任务记录保存策略版本与数据快照水位,对象存储只接受导出服务身份写入,下载接口重新授权后签发短期地址。任何一步只信任上一步传来的 accountId,都可能把定位参数误当授权证据。
故障实验在任务排队后撤销用户权限:若业务承诺“发起时授权”,快照必须不可扩大且下载仍重新检查;若承诺“执行时授权”,worker 在读取数据前重算权限。两种选择都要写清并可测试,不能由队列延迟偶然决定。审计串起 requestId、jobId、policyVersion、queryScope 与 objectKey,且不记录导出正文。
模型验收还要请领域 owner、安全、平台与运维分别挑战资产、边界、运行身份和恢复路径。任何“网关已经处理”“内网不会伪造”“数据库账号只能应用使用”的假设都要找到配置或运行证据,否则仍是未闭合风险。
上线后用真实拒绝事件校正模型:若大量跨边界请求落到未分类原因,说明执行点或威胁枚举仍有空白;若控制从未产生证据,先验证它是否真正位于流量路径,而不是直接判断系统没有受到尝试。季度复审只看文档更新时间没有意义,应抽取一个资产,从入口到权威写入重放完整证据链,并确认人员、服务账号、批处理和应急入口都服从同一不变量。复审结论还要落实为新增断言、策略变更或有期限的风险记录,不能只留下会议纪要,也不能以负责人经验代替可重复验证。
安全状态机必须让失败停在安全位置
一次安全决策沿“解析—认证—授权—执行—审计”推进。任何阶段超时或证据不足都不能伪装成允许;同时要区分未认证、无权限、输入拒绝、依赖不可用和结果未知,避免客户端盲目重试。
反向验证要证明旁路也被关闭
测试不止提交非法字符串,还要更换租户、对象、HTTP 方法、内容类型、转发头、异步线程、重定向目标和旧版本凭证;在认证服务、审计存储、DNS、密钥源和策略缓存超时时观察默认行为。拒绝必须无业务副作用,错误响应不泄露资源存在性或内部实现。
容量同样属于安全边界。密码派生、签名验证、文件扫描、令牌内省和审计写入都可能被放大;为主体、租户、IP、操作和下游分别设置预算,并记录拒绝原因。限流不能只按可伪造 Header,也不能让攻击流量占满合法用户恢复通道。
生产证据与治理
上线门禁要求:威胁场景有 owner;允许和拒绝路径都有测试;默认拒绝与应急旁路已演练;秘密可轮换;审计可查询且防越权;依赖与构件来源可证明;旧版本兼容窗口和撤销路径明确。安全控制只有在故障、升级和应急状态仍成立时才算完成。
用两个 Java 17 模型固定安全不变量
模型不尝试实现密码算法或攻击载荷,只把控制输入、判定顺序和确定输出固化,真实系统再替换为框架、密钥服务与持久化策略。
javac --release 17 -Xlint:all -Werror examples/backend-development/security/security-model/TrustBoundaryDemo.java examples/backend-development/security/security-model/ThreatPriorityDemo.java
java -cp examples/backend-development/security/security-model TrustBoundaryDemo
java -cp examples/backend-development/security/security-model ThreatPriorityDemoassets=4 boundaries=3 untrustedFlows=2 controls=4 residualRisks=1
likelihood=4 impact=5 exposure=3 priority=60 decision=BLOCK_RELEASE输出变化必须对应一条明确策略或迁移决定;禁止为了让测试通过而放宽 issuer、audience、tenant、路径、算法或来源校验。
把控制成本写进容量与事故恢复
“任何跨边界数据都必须重新建立身份、完整性、授权与资源预算”不是免费承诺。认证与密码派生消耗 CPU,远程策略和 Session 占用连接,签名与哈希增加计算,扫描和隔离消耗磁盘与队列,审计增加写放大。容量模型按峰值合法流量叠加恶意放大:每请求验证次数、下游往返、最大输入、并发隔离任务、失败重试、日志体积和安全状态保留期。平均延迟正常而拒绝队列持续增长,仍然意味着控制即将失效。
事故恢复按“阻止继续扩大—保存证据—撤销资格—修复权威状态—重建派生状态—验证不变量”推进。只修代码却不撤销 token、secret 或错误策略缓存,攻击窗口仍然存在;只轮换凭证却不检查已发生业务写,也无法证明资产恢复。对 TrustBoundaryDemo 与 ThreatPriorityDemo 对应的决策输出建立线上指标或审计查询,才能把模型与生产证据连接起来。
升级时先部署严格兼容的读取端,再切换签发者或写入端,观察旧版本使用归零后删除兼容分支。任何临时兼容都记录 owner、截止、拒绝指标和回滚条件;为了兼容而跳过验证不属于迁移策略,而是新增旁路。
