应用架构安全审查要回答的是:在大量实现细节固化错误假设之前,当前设计是否真的能兑现安全承诺。团队选择目标架构、拆分服务、接入新的身份提供商、迁移敏感数据或新增异步流程时,这类审查最有价值。它的成果应当是工程师可以落实的设计决策,以及风险负责人能够验收的验证方案。

设想一个虚构的多租户工程设计文件平台。项目成员请求系统为外部合作方生成一个大型 CAD 归档包。API 检查该成员的权限,然后把任务放入队列。工作进程开始前,项目负责人移除了该成员,也撤销了合作方的访问权。工作进程随后仍然生成归档包和分享链接,因为任务进入队列时请求是合法的。

单看每个组件,似乎都没有明显故障:API 完成了用户认证,队列投递了格式正确的消息,工作进程执行了收到的任务。真正没有解决的是架构问题:授权应当绑定在提出请求的时刻、披露数据的时刻,还是由一个范围狭窄且有期限的委托权限跨越两者?架构审查需要明确选择,分析失败路径,并说明如何证明所选行为确实实现。

这与范围较广的应用安全审计不同,后者可能同时查看架构、代码、配置、API 和已部署系统。它也不同于源代码安全审计,后者把安全决策追踪到具体代码版本。架构审查发生得更早,关注的是组件关系、权限、数据流、故障语义与设计方案之间的取舍。

先明确审查要支持什么决策

“审查架构”还不能构成范围。应当写清待决事项:批准一项设计、在两种身份方案中选择、判断迁移是否降低风险,或者确定发布前必须满足的条件。记录决策负责人、需要作出决定的时间,以及最重要的安全结果。原型阶段可以保留一些实现问题;同样的问题在生产发布前可能足以阻止上线。

需要建立两套基线。现状描述真正运行的系统,包括临时桥接、人工操作、继承而来的信任,以及文档中没有记录的集成。目标状态描述拟采用的设计和抵达目标的迁移过程。把两者混在一张图里会产生错误结论。目标图可能为每个服务画出了独立身份,而迁移期间仍有部分调用使用共享凭据。反过来,当前缺陷也可能已经有预算明确、能够验证的替代方案。

输入材料不必精美,但必须具体。通常应包括组件图与部署图;用户、工作负载和管理员身份;数据分类与存储位置;同步和异步流程;外部供应商;密钥管理;部署和支持路径;关键业务不变量;已知限制和历史发现。材料之间若有冲突,审查者应记录差异,不能默认采用看起来最新的那一份。

因此,第一项成果是一组需要回答的问题,而不是漏洞列表。哪个主体有权造成哪一种结果?决策在哪里执行?哪个组件能改变规则?身份被撤销、消息重复、依赖不可用或恢复人员介入时会发生什么?哪些内容来自观察,哪些仍然只是推测?

分开绘制现状与目标的信任边界

有用的模型应显示参与者、组件、数据存储、数据流、信任区域与假设。信任边界不只是网络分界。信任依据发生变化的地方都会形成边界,例如浏览器到 API、工作负载到工作负载、租户服务到共享平台、客户账户到供应商控制面、生产服务到支持工具,以及队列生产者到消费者。加密可以保护连接,却不会自动定义授权关系。

对每次跨越都要记录调用方身份、接收方身份、双方如何认证、传递了什么权限、丢失了哪些上下文,以及最终由哪一侧执行授权。网关可以验证令牌签名,但下游服务仍需确认令牌是否发给自己,以及当前操作是否得到允许。在存在代理、负载均衡、动态地址和多个集群的环境里,网络位置不能可靠代替工作负载身份。

控制面也必须画出来。某个服务的数据面 API 权限很窄,但部署身份可能可以替换镜像、修改密钥、改写路由,或给运行进程绑定更高权限的角色。管理员也许无法通过产品界面查看客户记录,却可以创建数据库快照。审查需要把这些控制面能力与负责人、审批路径、紧急访问、日志和撤销机制连接起来。“内部”既不是一种身份,也不是授权策略。

左右滑动查看对照

架构视图审查要解决的问题可用于回答的材料结论的边界
组件与信任区域哪些组件相互信任,信任依据在哪里变化?现状和目标图、接口契约、部署清单图纸表达设计意图,不能证明真实网络可达性
身份与权限哪个用户、工作负载、支持人员或部署身份可以造成敏感结果?令牌设计、角色和策略、服务身份签发、管理流程认证成功本身不能证明授权正确
数据生命周期敏感数据在哪里收集、派生、缓存、索引、记录、备份、导出和删除?数据清单、保留规则、存储与缓存设计、备份和删除流程保留政策不能证明每个副本和日志都遵守政策
执行与故障调用超时、消息重复、依赖失败或人工重试时,行为如何变化?时序图、队列设置、重试与死信策略、恢复手册正常路径图不能说明故障行为
交付与控制面谁可以改动代码、配置、身份、路由和密钥?构建部署设计、高权限角色、审批与回滚路径代码仓库的控制不能证明生产环境的控制

一直追踪到数据真正消失

“静态加密”不是数据生命周期。对每一类敏感数据,都要追踪其收集、校验、使用、派生副本、缓存、搜索索引、遥测、支持导出、备份、归档和销毁。每个阶段都应有负责人和保护要求。删除设计还要说明副本、对象历史版本、搜索文档、队列载荷,以及从旧备份恢复之后如何处理。

这个过程常常会改变原本的架构问题。团队可能在争论数据库加密,而更大的暴露来自长期保留的分析导出,或向大量支持人员开放的诊断日志。另一个团队也许已经尽量减少主记录,却让同一批标识符长期留在死信队列里,没有人负责保留期限。审查应当区分服务必需的数据、有明确理由保留的数据,以及仅仅因为无人设计结束方式而存在的副本。

把异步工作当作一条授权路径

队列、调度器、Webhook、事件总线和工作流引擎既跨越组件,也跨越时间。生产者与消费者不共享一次请求的完整上下文,用户可能已经离线,策略也可能在结果发生前改变。因此,消息必须有明确的安全含义:它只是要求接收方重新判断一次操作,是已经批准且不可变的命令,还是允许在截止时间前完成一个特定结果的受限委托?

投递语义同样重要。采用至少一次投递语义的队列,例如 Amazon SQS 标准队列,可能重复投递消息。工作进程可能已经完成外部操作,却在确认消息前崩溃;可见性超时后,另一个进程会再次执行。反复失败的消息可能进入死信队列,之后由人员或工具重放。架构必须定义幂等性、去重范围、顺序假设、任务最大存活时间、重试上限、异常消息处理,以及谁有权重放或编辑失败任务。

回到虚构的 CAD 导出。队列消息不能只携带项目编号和用户原来的角色。设计必须说明工作进程如何获知允许访问的项目、收件方、数据范围、有效期和动作,也必须定义撤销的含义。如果政策要求在披露时仍具备访问权,工作进程就需要重新获取授权决定,并在策略服务不可用时得到安全结果。如果业务明确允许已批准的任务继续完成,系统就需要一个范围狭窄、有效期短的委托凭据,并用审计记录清楚显示这一例外。

从架构主张到可验证行为
  1. 建模分别画出现状和目标中的参与者、存储、控制面、同步调用、队列与信任变化
  2. 决策写出必须保持的不变量,比较方案,指定负责人,并记录剩余风险
  3. 细化把选择转化为身份、授权、数据、重试、撤销和可观测性要求
  4. 验证为每项主张分配审查、自动检查、集成测试、故障测试或经过授权的运行时验证
  5. 复审信任边界、供应商、敏感数据流或控制面权限变化时重新审查

分布式应用的每条服务链都需要这种处理。微服务渗透测试指南进一步说明了运行时测试如何确定范围;架构审查则应在测试尝试突破关系之前,先明确这些关系应该是什么。

用明确标准比较不同方案

安全审查不应把审查者偏好的模式变成通用规则。NIST 的系统安全工程方法把设计看作一个选择空间:定义目标结果和可衡量的选择标准,识别备选方案,逐一评估,再保留决策理由。安全只是标准之一,还要同时考虑可用性、延迟、运维复杂度、成本、隐私和迁移风险。

对于导出流程,至少有四种合理模型。每一种都可能适合特定情境,也都会产生不同的验证义务。

左右滑动查看对照

设计方案安全收益运维代价或新增风险决策需要的证据
工作进程执行时重新授权撤销和最新策略在敏感效果发生前生效策略服务故障会阻塞任务;结果可能不同于提交时的预期撤销、策略超时、租户绑定、安全失败和重试一致性测试
入队时签发范围狭窄、短期有效的委托已批准动作保持稳定,工作进程不需要宽泛的环境权限委托被窃取、范围过大、期限过长或重放会扩大权限受众、动作、资源、收件方、期限、必要时的发送者绑定,以及一次性或幂等行为
先生成文件,释放前重新授权昂贵的准备工作可以继续,披露仍遵循当前策略敏感文件会积累;清理与竞态处理更复杂存储隔离、释放检查、过期和删除、撤销与释放并发测试、运维访问审查
策略变化时取消待处理任务撤销能够传播到未完成工作,用户理解也较容易取消事件可能丢失、乱序或延迟,竞态仍然存在关联覆盖、竞态测试、定期对账、可观测的取消延迟和安全人工恢复

所选模型应写成可检验的不变量。例如,如果要求撤销立即生效,可以写成:“系统处理每次下载请求时,只有项目当前仍允许该合作方访问,才返回归档内容。”这意味着每次下载都要取得可靠的授权决定,并处理策略不可用、撤销与下载并发等问题。若选择短期委托,则不变量应明确委托的动作、资源、期限和撤销窗口,不能把“签发链接时检查过”表述成“之后每次下载都会检查”。

身份设计也需要同等精度。对用户联合登录,要把身份提供商给出的身份断言与应用自己的授权决定分开。对服务间调用,要写明调用者和预期接收者。使用 OAuth 访问令牌时,应把权限和受众限制到所需资源服务器、资源与动作,并要求接收方在每次请求时验证这些限制。一个被多个内部服务接受且权限宽泛的令牌,可能使失陷组件在该令牌授予的权限范围内继续访问其他服务。

同样的推理也要应用于管理路径。谁可以修改租户策略、签发服务身份、重放死信消息、恢复备份,或者绕过失效的依赖?紧急访问可能确有必要,但它的条件、持续时间、审批和证据应属于设计,而不是留作没有记录的运维捷径。

把已验证事实与假设分开

所有架构都有假设。问题不在于存在假设,而在于把假设当成已经工作的控制。建立假设台账,记录陈述、负责人、理由、错误时的后果、验证方法、期限和状态。状态可以是已验证、部分验证、未验证和已推翻。“队列是私有的”过于模糊;“在生产账户中,只有导出 API 的工作负载身份可以向该队列发送消息”则可以对照策略和真实部署进行检查。

有些假设是设计约束,例如交互登录依赖身份提供商可用。有些属于环境,例如托管密钥服务提供特定边界。有些描述行为,例如消费者会幂等处理每个任务。还有些属于迁移,例如第二阶段结束后不再保留共享凭据。不同类别需要不同证据。

验证方案应随目标设计一起交付。文档审查可以确认接口契约要求受众和有效期;配置分析可以把真实策略与预期调用图比较;单元和集成测试可以覆盖租户绑定、错误受众、撤销和重复投递;面向故障的测试可以覆盖超时、部分完成、陈旧策略、重试风暴和死信重放;可观测性检查则可以确认拒绝、特权变更和重放操作是否留下可用记录。

证据必须与主张对应。正常流程成功不能证明系统会安全失败;政策文件不能代表已部署策略;面板上没有拒绝记录也不能证明拒绝机制生效。架构审查可以规定需要什么证据,也可以查看现有材料,但等待实现或运行时确认的内容必须标为未验证。

对于 API 密集型系统,REST API 安全评估指南说明了如何比较契约与实际行为。对于多步骤不变量,业务逻辑安全测试指南解释了为什么每个请求单独合法,组合结果仍可能违规。运行时方法可以关闭特定验证事项,却不能替代设计决策。

验收决策记录,而不是通用清单

有用的报告应让架构师、开发人员、运维人员和风险负责人得到同一幅图景。报告应包含决策和范围;现状与目标图;参与者及权限矩阵;敏感数据生命周期;同步、异步和管理路径;假设及状态;与设计元素关联的威胁;考虑过的方案;所选控制;剩余风险;以及带有负责人和验收标准的验证方案。

结果应区分四种性质。设计缺陷表示现有或拟议架构违反了已认可的安全目标。待定决策表示仍有可信的替代方案,需要负责人选择。未验证假设表示结论依赖尚未获得的证据。实现验证事项表示设计已经明确,但代码、配置或已部署行为仍需确认。把四者混成一个严重性列表,会促使团队用工单标题来“修复”一个尚未回答的问题。

报告还必须说明没有证明什么。文档审查无法证明真实网络可达性、每个服务都验证令牌、每个副本都完成删除,或真实故障下的行为。它也不能认证系统已经安全。它的价值在于可追踪性:每项关键承诺都能连接到设计决策、负责组件和验收所需证据。

复审触发条件也应成为交付物的一部分。信任边界移动、新增身份或管理员、敏感数据获得新去向、同步调用改为排队、供应商获得更多权限,或恢复路径发生变化时,都应重新查看模型。否则,目标图很快只会成为历史插图,真实系统则在别处继续演化。

在验证方案要求时检查运行中的系统

当设计主张取决于已部署行为时,运行时验证很有价值。例如:服务是否拒绝受众错误的令牌;队列重试后是否仍执行租户隔离;权限已撤销的合作方能否取得先前准备的文件;支持人员能否通过管理路径接触数据。这些问题可以放入经过授权的Web 应用渗透测试范围,并配备适当账户、角色、测试数据、环境和安全限制。

Pentest.red 可以把选定的架构验证事项整理成边界明确的测试范围。渗透测试范围规划页用于在开始前记录系统、身份、流程、排除项、允许的操作、测试窗口和预期证据。测试必须取得所有受影响系统所有者的书面授权。结果只说明约定环境、角色、条件和时间内观察到的行为;它不是认证,也不能证明所有架构弱点都已被找到。

当团队无需依赖一张装饰性图表,就能解释以下问题时,审查才真正完成:哪条边界最重要,谁的权限跨越它,数据和排队任务会经历什么,选择了哪种方案,哪些假设仍未关闭,以及什么证据能够说明实现后的系统兑现了承诺。

PENTEST.RED

讨论渗透测试范围与报价

首先明确系统、访问权限、限制条件和预期证据。

准备测试范围

本文以深入研究为基础,经过编辑整理,注重清晰表达和实际应用。

PENTEST.RED / RED JOURNAL