下面的场景是虚构的,但由一些常见细节拼成。某平台团队维护着一个内部对象存储模块,约三十个服务依赖它。为了方便一个需要公开访问静态资源的团队,有人在模块里加了 public_read 变量。默认值忘了设为 false,评审人只看了改动的几行,没注意到默认值已经变成放行。流水线里的扫描器报了警,但这条规则早就因为“太吵”被改成了建议模式。一周后,所有使用方都更新了模块,一部分存放导出数据和备份的存储桶变得可以从外部读取。没有人突破边界:基础设施只是忠实地执行了代码里写的内容。

这个故事里没有哪一个“漏洞”是端口扫描器能发现的。存在的是一串决策:共享模块怎么设计、默认值是什么、谁以及如何审批变更、流水线如何处理策略告警、模块修复多快扩散到各个环境。这正是基础设施即代码安全评估应当检查的内容。下文介绍这类审计包含什么、如何把通用测试方法用到它身上、自动化在哪里失效,以及如何委托一项结果可复现、可比较的工作。

IaC 评估与检查运行中服务器有何不同

传统技术检查面向在线系统,一台主机接一台主机、一个服务接一个服务地看。基础设施即代码评估看的则是这些主机的来源:Terraform 或 OpenTofu 模板、CloudFormation、清单文件和 Helm Chart、可复用模块、变量文件、CI/CD 流水线定义以及策略即代码(policy-as-code)规则。它还要看把代码变成网络、账号、存储和负载均衡器的交付流程。

因此审计要回答两个问题。第一,所描述的基础设施是否实现了组织认定必须具备的安全属性,例如默认关闭的存储、最小化的角色、已开启的日志和加密?第二,流水线能否在不安全变更进入生产环境之前把它拦下,还是只在一份没人看的日志里提一句?

IaC 有一个改变检查经济性的特点:决策是集中的。热门模块里的一个错误,会在所有引用它的环境中重复出现。但同一特点也让修复得以规模化。修正模块默认值有助于消除共同根因。对每个使用方,还需确认更新已应用、不安全的覆盖配置已移除,并且实际资源已达到预期状态。这是对 IaC 本质的观察,而非统计数据,但它决定了该在哪里深挖、如何归并发现项。

同样重要的是,不要高估代码分析能证明什么。对仓库的静态检查显示的是“被描述的”内容,并不能证明在线环境与描述一致:资源可能被人在控制台手工改过,部分控制继承自组织或账号层面的策略,某些值只在应用(apply)时才出现。因此成熟的评估总会注明每条证据来自哪个层面:代码、变更计划、测试环境,或在单独授权下的已部署环境。

审计范围:哪些在内,哪些需要单独授权

IaC 评估的范围远不止“模板目录”。它包括声明式模板和可复用模块(含内部模块目录);变量文件、各环境的覆盖配置和参数来源;负责规划、审批和应用变更的流水线定义;策略即代码规则及已批准例外的登记册;以及状态文件、密钥和流水线日志的处理方式。模板定义的边界配置值得单独一提:网关、负载均衡器和 ingress 控制器上的 HTTP 响应头,如 Content-Security-Policy、CORS 规则和 Permissions-Policy。只要这些响应头写在代码里,它们就和网络访问规则一样成为可检查的对象。

同样重要的是,未经明确书面授权,哪些不在范围内:任何针对生产系统的操作、对共享账号、订阅和租户的修改,以及对第三方服务和云服务商的测试。授权和工作规则应在技术工作开始前确定,而不是边做边定。其中应写清:允许部署和检查哪些环境、发放哪些凭据、谁可以叫停工作、发现密钥后如何处理。

另一点是检查人员会看到的数据。状态文件和计划输出中可能包含内部资源地址、标识符,有时还有密码和密钥。项目期间这些数据存放在哪里、结束后如何删除,属于范围的一部分,而非走形式。

如果你的 IaC 主要描述集群基础设施,部分问题会与 Kubernetes 安全审计 重叠:那里把清单、RBAC 和网络策略当作运行中集群的对象来检查,这里则把它们当作构建集群的代码来检查。

方法:把 NIST SP 800-115 用到模板和流水线上

本文采用的参考资料都不能单独构成完整的 IaC 评估方法。一个方便的起点是 NIST SP 800-115《信息安全测试与评估技术指南》,2008 年 9 月发布,取代了 SP 800-42。它说明了如何规划和实施技术测试、分析结果并制定风险缓解策略,并指出此类测试的两个目的:发现漏洞,以及验证是否符合策略或其他要求。两者对 IaC 都适用,而且如下文所述,最好不要混为一谈。

它是方法概览而非完整方案,也没有讨论 IaC,因此其逻辑需要转译,而不是照搬。

规划、执行、执行后

从形式上看,SP 800-115 把评估分为三个阶段。规划包括范围、书面授权、目标和工作规则。执行即检查本身:对 IaC 而言,是审阅模板、模块和流水线定义,分析变更计划,并在约定环境中进行非破坏性检查。执行后阶段包括结果分析、报告和风险缓解建议。

对 IaC 来说,规划还包括盘点:仓库、模块、流水线、各自的负责人和环境。没有盘点,既无法选择检查深度,也无法追踪修复是否到达使用方。

实用的整改循环

在正式阶段之上,最好再维持一个实用循环;这是作者的建议,并非标准的一部分。每个发现项都转化为带有根因和负责人的风险描述。接着是代码层面的修复,最好改在模块里,而不是只改某一个使用方。然后是降低复发风险并检测其覆盖场景的防护机制:策略规则、模块测试、流水线中的强制检查。最后是复测,并记录剩余风险,包括带有效期的例外。

IaC 发现项很容易对应到 NIST 为 SP 800-115 关联的控制族,例如审计与问责、系统完整性、通信保护、风险评估、系统与服务采购:即日志、网络边界、流水线完整性和模块来源。

威胁模型:基础设施代码的风险集中在哪里

IaC 的威胁模型不必冗长。只需几个领域,每个领域都对应组织的目标成果和根因类别,就能让优先级排序前后一致,而不是凭感觉。

身份、权限与网络暴露

代价高昂的错误在 diff 里可能看起来毫无害处:一个“暂时”带通配符权限的角色、一把长期有效的服务账号密钥而非联合身份、一条地址范围很宽的入站规则、一个默认创建为公网可达的负载均衡器。模块里的每一行这样的配置,都会乘以使用方的数量。默认数据保护也属于这一类:加密关闭、未设置日志保留、存储访问审计被禁用。

日志值得特别关注。CISA 的 Secure by Design 指南把日志列为厂商应默认提供且不额外收费的能力。共享 IaC 模块实质上是面向内部团队的产品,对它适用同一原则是合理的:审计日志应由模块开启,而不是交给每个使用方各自处理。这是我们的类比推论,而不是 CISA 针对 IaC 的专门要求。

密钥、状态文件与流水线日志

IaC 中的密钥并不只会通过提交的变量文件泄露:某个值可能以明文进入状态文件,通过计划输出进入构建日志,或进入云服务商插件的调试日志。因此只问“仓库里有没有密钥”是不够的。需要追踪密钥的路径:它从哪里来、状态存放在哪里、谁能读取状态后端和构建产物、日志保留多久。

作为攻击面的流水线

负责应用基础设施变更的流水线,持有组织内最强的一些凭据。如果不可信输入能够触及它,攻击者就不需要云访问权限,只要让流水线替他做事即可。

典型机制是把不可信数据插入 shell 命令的步骤:分支名、pull request 标题、文件名。这属于操作系统命令注入类别,CISA 为此专门发布过一份 Secure by Design 警示。其关于路径遍历的警示,同样适用于流水线按依赖输入的路径解压或复制产物的场景。第三方模块和插件是另一个话题:未固定的版本或指向可变分支的引用,意味着模块内容可能在未经评审的情况下改变。NIST SP 800-204D 讨论了如何把软件供应链安全融入 CI/CD 流水线;关于这部分的检查,可参阅我们关于软件供应链安全评估的文章。

不可信输入如何经由流水线触及基础设施(示意图,非实测数据)
  1. 外部 pull request攻击者从 fork 提交 PR,并在分支名或标题中放入一段 shell 命令片段。
  2. 插入 CI 步骤规划步骤未经转义就把该值插入 shell 命令,片段在 runner 上被执行。
  3. runner 凭据由于密钥没有与不可信事件隔离,runner 能拿到可读取状态或应用变更的令牌。
  4. 泄露或篡改攻击者读取含密钥的状态文件或修改模块,变更随之扩散到所有使用该模块的环境。

检查人员会查看:哪些事件触发哪些作业、来自 fork 的作业能拿到哪些密钥、规划与应用是否分离、谁来批准生产环境的人工审批。这些是技术信任边界问题。流程问题,例如谁负责策略、审批如何记录,则由流程审计来检查,那是另一项工作,需要另一类证据。我们在DevSecOps 流程审计一文中有详细介绍。

代码与现实之间的漂移

最后一个领域是描述与部署之间的差异。事件处置时在控制台的手工修改、绕过 IaC 创建的资源、覆盖模板的组织级设置,都会让代码成为不完整的事实来源。漂移分析回答的不是“代码好不好”,而是“能否凭代码判断环境”。在多云环境中,还要叠加各云服务商在权限模型和默认值上的差异,因此这部分最好与多云安全评估一并进行。

评估方法及其各自看不到的部分

没有哪种方法能覆盖全貌,SP 800-115 也明确强调每种技术都有其优势和局限。好的评估会组合多种方法,并如实说明每条证据来自哪种方法。

关于下表的两点实务说明。计划分析需要权限受到严格限定的非生产环境凭据,计划输出本身也必须作为机密数据保存。测试环境验证也涵盖边界:如果模板在 ingress 上设置了安全响应头,就在测试环境确认这些响应头确实被返回。

左右滑动查看对照

方法能提供什么看不到什么适用时机
模板和模块静态分析尽早发现模块所有使用方中的常见配置错误应用时才确定的值、继承的组织策略、手工修改持续在流水线中运行,并作为审计的第一轮
变更计划分析只有代入特定环境变量后才出现的问题IaC 之外创建的资源;运行时行为应用到敏感环境之前,使用非生产凭据
测试环境验证确认所描述的控制确实已部署并生效生产环境的差异:数据、规模、手工修改、继承设置用于关键的身份、网络和数据存储模块
流水线与变更权限审查信任边界:事件、密钥、审批、规划与应用的分离模板内部的逻辑错误只要流水线拥有应用变更的权限就应进行
基于威胁的人工审查危险的配置组合和模块设计缺陷覆盖所有仓库的完整性用于复用最广、数据最敏感的模块

关于测试环境需要说明一点:测试环境中得到的结果不能原样套用到生产环境。测试环境证明的是模块在给定参数下“能够”部署安全配置。生产环境中实际部署了什么,只能由生产环境自己的数据来说明:漂移分析、配置导出,或在单独授权下的检查。

标准是支撑,而不是装饰

引用标准的价值,恰恰在于它能帮助复现结论或比较服务商;封面上的一串缩写什么也证明不了。对 IaC 而言,有几个来源确实有用,各自作用于不同层面。

NIST 网络安全框架(CSF)2.0 主要在管理层面发挥作用:目标配置文件(Target Profile)描述组织追求的成果,代码依据这些成果来检查,而不是依据扫描器恰好自带的一套规则。这样,向管理层的汇报讲的是成果,而不是工具输出。

OWASP 应用安全验证标准(ASVS)为应用的技术安全控制提供可验证的要求,其中包括应用所依赖的环境中的控制,而这正是 IaC 所描述的环境。对于 ASVS 5.0.0,项目建议用带版本号的标识符引用要求,例如 v5.0.0-1.2.5,因为编号可能在版本之间变化。这样就能把依赖环境的要求与实现它的具体模块关联起来。

CWE 提供了根因词汇表。按类别归并发现项,例如命令注入、路径遍历、权限分配不当、敏感信息泄露,可以看出哪些错误在各仓库中反复出现,从而修复整个类别而非单个实例。如果报告使用 CWE Top 25 之类的排名,应注明其发布年份。

CISA 的 Secure by Design 原则为共享模块的负责人提供了参照:安全是默认属性,而不是需要使用方记得开启的选项。NIST SP 800-204D 则为把供应链安全融入 CI/CD 流水线提供了框架,当评估从模板延伸到产物如何构建和交付时,它很有用。

关于版本纪律。SP 800-115 发布于 2008 年,而 ASVS 和 CWE 排名会更新。未注明版本的引用会使一年后的复现和比较产生歧义,因此每条引用都应固定到版本或年份,也不应把方法论文件说成法律要求。

优先级:看根因,而不是数发现项

发现项数量是衡量 IaC 评估质量的糟糕指标。如果同一条规则在共享模块的一百个使用方中命中,这意味着一个共同根因和一百个受影响实例:报告应归并根因,同时保留各使用方的暴露情况和整改状态。反过来,流水线定义中一行把密钥暴露给 fork 作业的配置,分量可能超过报告其余部分的总和。

因此应按根因归并发现项,再依据受影响范围、可利用性、影响和补偿措施确定优先级。共享模块和流水线缺陷、外部暴露及权限扩大,都是这一决策的独立输入。不会造成可利用状态的内部策略偏差应单独归为一组。SP 800-115 把漏洞发现和合规验证列为不同目的,报告中也应将两者分开:混在一起的清单会模糊优先级,团队会先修容易的,而不是更危险的。

IaC 评估最有价值的成果,可能不是一份长清单,而是一个加固后的模块或一条策略规则,使安全配置成为默认选项。这正是 Secure by Design 的实践含义:安全决策在模块中做一次,而不是在每个团队重复。如果无法提供安全默认值,例如使用方确实需要不同模式,那么不安全的选项应当要求明确、有意识的选择,而不是像 public_read 的故事那样悄悄开启。

取舍:阻断、告警还是先度量

自动化策略检查可以扩展到数百个仓库,但噪声大。如果没有一份例外登记册,为每个例外写明负责人、理由和有效期,临时例外就会变成永久的,规则实际上形同关闭。文章开头故事里,扫描器告警正是这样被所有人忽略的。

阻断式检查能拦下高风险变更,但可能拖慢交付并诱发绕行。建议式检查保住了速度,却取决于是否有人阅读告警。CISA 的《安全软件部署》(Safe Software Deployment)指南强调部署流程应包含可靠的测试和度量环节。实践中,这支持循序渐进的做法:先度量规则的表现和误报数量,再把高风险、低噪声的规则切换为阻断模式,其余保持建议模式,并由例外登记册加以管控。

广度与深度同样相互竞争。对所有仓库做一遍浅层扫描,能找到常见配置错误;而对少数身份、网络和数据存储模块做深入分析,能发现自动化遗漏的设计缺陷。合理的计划把深度投向复用度和数据敏感度最高的地方。

最后,一次性评估与持续防护机制解决的是不同问题。前者提供有依据的基线,后者负责守住它。采购一次性评估时,应提前问清:基线发现项将如何转化为长期有效的检查,而不是停留在邮箱里的一份 PDF。

如果你正在考虑把什么交给外部检查,是几个关键模块、变更应用流水线,还是整个模块目录,最好提前讨论这些边界。你可以在渗透测试范围页面描述仓库、环境和限制条件,Pentest.red 可以帮助你判断哪些方法和哪些证据值得纳入。

如何验收修复并进行复测

IaC 发现项应转化为有明确负责人的代码变更,而不是控制台截图。如果修复落在共享模块里,工作并不随合并而结束:需要追踪哪些使用方仍停留在旧版本,否则修复只存在于模块仓库中。

复测不仅要确认某一行确实改了,还要确认防护机制能在记录的条件下检测其覆盖的场景。关于如何证明修复真正有效,详见我们关于渗透测试整改验证的文章。

左右滑动查看对照

发现项类别可接受的修复证据复测时如何检查应记录的剩余风险
共享模块中的不安全默认值采用安全默认值的新模块版本,以及在旧值恢复时会失败的测试对多个使用方做变更计划分析;列出仍在使用旧版本的使用方尚未更新的使用方及其迁移期限
流水线或服务角色权限过大收窄后的权限策略,以及禁止通配符权限的规则在非生产环境分析有效权限;在测试环境尝试被禁止的操作因合理例外保留的权限,附负责人和期限
流水线步骤中的命令注入通过安全 API 把值作为数据传递,或使用正确引用且不会被 eval 或 shell 再次解释的环境变量在测试流水线上用安全载荷重放原场景;检查同一模板的相邻步骤未能迁移的流水线模板及补偿措施
状态文件或日志中的密钥轮换密钥、迁入密钥管理服务、限制对状态后端的访问在新的状态和日志中搜索该值;检查谁能读取后端旧密钥暴露的时间段及后续处置决定
未固定的第三方模块或插件固定版本或哈希、仓库中的锁文件、策略规则确认使用未固定来源时构建会失败没有受支持版本的模块及其替换计划

运营指标应提前选定:通过约定策略的模块比例、按严重程度划分的修复时长、过期例外的数量。这些是推荐做法而非基准值,应依据自身的目标配置文件来定义。机器可读的 ASVS 要求清单便于用要求标识符标记策略规则,CWE 标识符则便于按根因归并发现项。

如果分析中使用了基于 AI 的工具,其输出在影响整改或报告之前必须由合格的专业人员复核:一条看似合理、实则错误的“为何这条规则安全”的解释,和模块里的错误一样容易被复制。

如何选择 IaC 安全评估服务商

如果不清楚到底要检查什么,比较报价就毫无意义。两份面向采购方的文件可以帮上忙:CISA 的《Secure by Demand》指南,面向与软件和服务供应商的谈判;以及澳大利亚 ASD's ACSC 与 CISA 联合发布的关于选择安全且可验证技术的指南。两者都适合作为征求建议书(RFP)的框架。

ASVS 在这里同样有用:该项目声明其目标是统一安全验证服务的覆盖范围和严格程度。如果要求每家服务商说明覆盖了哪些要求和级别,并附带标识符和版本,报价就具备了可比性。

签约前值得提出一些实际问题。服务商依据哪些标准的哪些版本,每项在方法中用于什么?授权、工作规则和允许的环境清单如何形成文件?状态文件、密钥和计划输出在项目期间如何保护、结束后如何删除?结果是否以机器可读形式交付,并映射到 CWE 和带版本的要求标识符?发现项是否按根因归并,并给出模块层面的修复?复测以及流水线防护机制的协助是否包含在工作中?

危险信号也很好辨认:报告只有扫描器输出;引用标准不带版本;无法核实的认证、客户或成果声明;愿意在没有书面同意的情况下测试生产环境。最后一条足以让你放弃合作。还要记住,渗透测试或安全评估不是认证,也不是鉴证:它是在约定范围内、对检查时发现的风险给出的技术意见,并不保证风险不存在。

自建还是外包,取决于成熟度和独立性要求。策略即代码运行成熟的团队可以自己开展持续检查;外部评估则适用于建立第一条基线、高风险架构变更之前,以及策略或合同要求独立性的场合。一种可选方案是混合模式:外部基线评估加关键模块深度审查,随后由内部防护机制接手,并定期进行外部复测。如果这种形式适合你,可以在渗透测试服务框架下与 Pentest.red 讨论:基线评估纳入哪些模块和流水线、你将获得哪些证据,以及复测如何安排。

结语:让 IaC 评估在报告之后依然有用

基础设施即代码安全评估不是对仓库跑一遍扫描器,而是按照 NIST SP 800-115 的逻辑、应用于模板、模块和流水线的技术评估。它依据组织的目标成果衡量代码,按根因归并发现项并按风险确定优先级,追求安全默认值,而不是没完没了地反复发现同样的错误。

无论自行审计还是委托外部,都应坚持四点:边界清晰的书面授权、带版本的标准引用、按根因归并的发现项,以及模块层面的修复加上降低复发风险并检测其覆盖场景的防护机制。还要牢记证据的边界:代码、计划、测试环境和生产环境是四个不同的层面,诚实的报告总会说明每个结论来自哪一层。

PENTEST.RED

讨论渗透测试范围与报价

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

准备测试范围

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

PENTEST.RED / RED JOURNAL