CI/CD 流水线中的凭证一旦越过原有信任边界,风险就不再局限于某次提交:它可能出现在 Git 历史、构建日志、环境变量、缓存、制品包或部署目标中,并被拥有相应读取权限的人员或系统继续访问。有效的处置不能只删除一行配置,而应沿着凭证生命周期完成发现、判断、遏制、轮换、验证和复盘,最终让凭证回到可控的存储、使用和失效机制中。

先界定泄露对象与影响边界
排查的第一步不是立即修改流水线,而是确认“什么凭证、在哪里出现、谁可能读到、还能做什么”。常见对象包括:
| 暴露位置 | 需要确认的内容 | 主要风险 |
|---|---|---|
| 当前代码和 Git 历史 | 提交、分支、标签、Fork、镜像仓库和缓存中是否存在凭证 | 凭证可能被长期复制,即使当前文件已删除也不代表失效 |
| 构建日志 | 命令行参数、错误输出、调试信息、测试报告是否回显敏感值 | 具备日志读取权限的人员或系统可能获得凭证 |
| 环境变量和流水线变量 | 变量作用域、是否对所有任务可见、是否可被脚本打印 | 非必要任务获得过高权限,或因脚本错误被导出 |
| 制品与缓存 | 配置文件、打包目录、容器镜像层、缓存归档是否包含凭证 | 凭证可能随制品进入长期存储或下游环境 |
| 部署目标和第三方服务 | 凭证访问的云资源、数据库、代码平台、签名服务及其审计记录 | 影响可能超出单个项目,涉及数据、生产系统或供应链 |
如果发现的是云访问密钥,应同时判断访问密钥标识和秘密部分是否都暴露。以阿里云 AccessKey 为例,官方文档明确区分了 AccessKey ID 与 AccessKey Secret,并指出只有两者同时泄露且仍有效时,第三方才可能利用该访问密钥;这并不意味着单独暴露标识可以忽略,因为它仍可帮助关联账户、识别误配或触发后续告警。可参考阿里云 AK 泄露检测文档核对平台侧告警和处置入口。
记录证据,但不要复制秘密
事件记录应保存足以复核的元数据,而不是把完整凭证再次传播到工单、聊天群或邮件中。建议记录:
- 仓库、项目、流水线、任务和运行编号;
- 首次发现时间、最后一次可能暴露的时间;
- 凭证类型、所属主体、环境和权限范围;
- 暴露位置的文件路径、日志片段位置或制品版本;
- 相关提交哈希、构建编号、制品摘要和访问日志时间段;
- 已采取的限制措施、负责人和预计完成时间。
凭证值本身可以使用不可逆摘要、部分掩码或密钥管理系统中的内部标识替代。只有确有必要的授权人员,才应在受控系统中查看完整值。
以风险分级决定响应速度
泄露不等于已经发生滥用,但在无法证明凭证未被读取时,应按“可能已被使用”处理。风险判断至少包含以下维度:
- 权限范围:是只读单个测试资源,还是可以修改生产资源、读取数据库、管理身份或签发制品。
- 有效时间:凭证是否仍有效,是否长期存在,是否有过期时间和使用次数限制。
- 暴露可见性:公开仓库、公开制品和公开日志优先级最高;内部日志也不能视为绝对安全。
- 可追溯性:是否有云审计、代码平台审计、制品下载记录和流水线运行记录。
- 业务环境:开发、测试、预发布和生产是否共用主体或凭证。
- 数据与完整性影响:是否可能读取敏感数据、修改部署、替换制品或影响签名发布。
可以采用以下简化分级:
| 等级 | 典型情况 | 首要动作 |
|---|---|---|
| 紧急 | 生产写权限、管理员权限、签名凭证、数据库高权限凭证或公开可见的有效秘密 | 立即限制或禁用,冻结相关发布,启动事件响应 |
| 高 | 内部日志、制品或私有仓库中的有效写权限凭证,且读取范围不明确 | 尽快轮换,审计使用记录,限制流水线权限 |
| 中 | 短期、只读、单环境凭证,暴露范围明确且有完整审计 | 在限定窗口内轮换,补充检测和验证 |
| 低 | 已过期、已吊销或测试专用凭证,仍需确认没有复用 | 保留证据,修复检测和配置,检查是否存在相同凭证 |
“已从代码中删除”不能直接作为低风险依据。Git 历史、分支、Fork、构建缓存和制品可能仍然保留旧内容;真正降低风险的关键是让凭证失效,并证明替换后的凭证没有继续泄露。
发现后的应急处置顺序
1. 先减少可用性,再清理表面痕迹
对于高风险凭证,优先在凭证所属平台执行禁用、撤销或权限收敛。不要先花大量时间重写 Git 历史,导致有效凭证继续运行。处置顺序通常如下:
- 暂停会继续使用旧凭证的流水线、自动部署或定时任务。
- 对高权限凭证临时禁用,或移除高风险权限。
- 保留必要的审计记录和运行证据。
- 在秘密存储系统中生成替代凭证,避免人工在脚本或聊天工具中传递。
- 更新流水线引用,并通过受控方式验证新凭证。
- 确认旧凭证已撤销或过期后,再清理代码、日志、缓存和制品中的残留。
如果立即禁用会造成生产中断,应先评估业务影响,并采用临时的最小权限替代凭证或短时维护窗口。对于无法即时撤销的第三方凭证,应先阻断相关任务、限制来源网络或缩小权限,同时向服务提供方申请紧急轮换。
2. 沿所有复制路径排查
不要只搜索当前默认分支。应根据事件范围检查:
- 所有活跃分支、标签和近期提交;
- 合并请求、代码评审附件和补丁文件;
- 仓库镜像、Fork、备份和导出包;
- 构建日志、测试报告、调试归档和缓存;
- 容器镜像层、压缩包、部署清单和配置文件;
- 制品库中的历史版本及其下载权限;
- 流水线变量、运行参数和任务输出;
- 可能复用同一凭证的其他项目和环境。
清理内容时要区分两件事:消除可见副本和使凭证失效。前者降低继续扩散的概率,后者才是阻断使用的核心。
3. 查审计记录,判断是否存在使用迹象
在凭证所属平台和流水线平台交叉核对:
- 凭证在暴露时间段是否被调用;
- 调用来源、时间、区域、用户代理或运行主体是否异常;
- 是否出现权限变更、资源创建、数据读取、制品下载或部署修改;
- 是否有失败次数激增、访问范围扩大或异常发布;
- 是否存在同一凭证在多个项目、环境或地区使用的情况。
审计记录缺失时,不应把“没有发现异常”表述成“没有被使用”。更准确的结论是:证据不足,需要通过扩大轮换范围、检查下游系统和补充监控降低不确定性。
设计可回滚的密钥轮换
密钥轮换不能只考虑生成新值,还要考虑应用切换失败时如何恢复服务。推荐采用“双凭证短窗口”或平台原生的版本化秘密机制:
- 生成新凭证,并赋予与旧凭证相同或更小的权限。
- 将新凭证写入秘密存储系统,不直接提交到仓库。
- 在非生产环境执行连接、部署和回归验证。
- 让流水线支持从新版本读取凭证,逐步切换任务或环境。
- 观察失败率、权限错误、部署结果和业务指标。
- 在确认新凭证稳定后撤销旧凭证。
- 保留明确的回滚入口,例如恢复到旧的秘密版本;但不要重新启用已经疑似泄露的旧凭证。
回滚安排应写进变更单或事件记录,至少说明:
- 哪些服务需要重启或重新发布;
- 哪些任务仍可能缓存旧值;
- 失败时恢复的是配置版本还是凭证本身;
- 回滚窗口多长,谁拥有批准和执行权限;
- 回滚后如何再次撤销疑似泄露的凭证。
如果旧凭证已经公开暴露,回滚只能回退应用配置或版本,不能回滚到继续使用旧凭证的状态。
将长期治理落实到流水线
最小权限:按任务和环境拆分
流水线不应使用一个可以管理所有环境的共享管理员凭证。可以从以下维度拆分:
- 按开发、测试、预发布和生产分别建立主体;
- 按构建、发布、读取制品和基础设施变更分别授权;
- 只允许任务访问所需的秘密,而不是将全部变量注入每个步骤;
- 生产发布增加人工批准、受保护分支或独立信任边界;
- 限制凭证的资源范围、操作类型、来源网络和有效时间;
- 对自托管运行器限制网络访问、文件权限和任务间残留。
权限收敛后应验证两种结果:目标任务仍能完成必要操作,非目标任务确实无法读取或使用该凭证。仅查看权限配置而不做受控验证,容易留下“配置看似最小、实际可横向使用”的问题。
优先使用短期凭证和受信身份
长期静态密钥应逐步替换为短期凭证、工作负载身份或平台间的受信联合机制。流水线在运行时获取临时凭证,任务结束后自动过期,可以降低 Git 历史、缓存和日志残留带来的长期风险。
采用短期凭证时仍需关注:
- 令牌是否被脚本打印或写入制品;
- 令牌生命周期是否覆盖完整任务但不过度延长;
- 任务重试是否会复用不应复用的值;
- 运行器退出后工作目录和环境变量是否清理;
- 失败任务的诊断日志是否包含认证请求参数。
短期凭证不是日志脱敏和权限控制的替代品。它只能缩短可利用窗口,不能消除泄露本身。
日志脱敏要覆盖命令、输出和错误
日志治理应同时处理流水线平台和脚本自身的行为:
- 避免使用会打印完整环境变量的调试命令;
- 不把凭证放进命令行参数、URL、提交消息或错误文本;
- 对固定秘密值、令牌格式和连接字符串配置掩码;
- 对测试报告、异常堆栈和归档日志执行同等处理;
- 限制日志下载、长期保留和跨项目访问权限;
- 对脱敏规则进行正向测试,确认不同输出路径不会绕过掩码。
掩码并不等于删除。若秘密已经进入历史日志,应按凭证泄露处置,先轮换,再清理或限制历史日志访问。
建立分层检测,而不是只依赖一次扫描
有效的凭证泄露检测应覆盖提交前、合并时、构建时和运行后:
| 检测位置 | 目标 | 处置方式 |
|---|---|---|
| 开发者本地或提交钩子 | 尽早阻止明显的密钥格式、私钥和连接字符串进入提交 | 提示修复,允许经过授权的误报豁免 |
| 合并请求和受保护分支 | 防止敏感内容进入主分支和发布路径 | 阻断合并,要求安全复核 |
| 全历史扫描 | 发现旧提交、标签、分支和镜像中的残留 | 触发轮换和清理流程 |
| 构建日志与制品扫描 | 识别运行时生成的配置、测试输出和打包文件 | 阻止发布或隔离制品 |
| 云平台与第三方服务告警 | 发现公开仓库中的有效凭证和异常使用 | 联动禁用、轮换和审计 |
| 持续审计 | 发现权限扩大、异常调用和秘密访问 | 进入安全运营或事件响应流程 |
检测工具的结果需要人工判断。误报处理不能通过关闭规则解决,而应记录:
- 命中内容属于什么类型;
- 为什么确认不是可用凭证;
- 是否仅在测试数据或文档示例中出现;
- 豁免范围是文件、规则、项目还是单次提交;
- 豁免是否有负责人、期限和复核日期。
对文档中的示例值、测试用假凭证和格式相似的随机字符串,应优先替换为明确的占位符,并在扫描器中使用精确、短期、可审计的豁免。任何无法证明为假值的命中,都应按真实凭证处理。
用检查表完成一次闭环
发现与判断
- [ ] 已确认凭证类型、所属主体、环境和权限。
- [ ] 已定位当前代码、历史提交、分支、日志、缓存和制品。
- [ ] 已记录首次和最后一次可能暴露的时间。
- [ ] 已评估公开性、可用性、权限和业务影响。
- [ ] 已指定事件负责人、审批人和沟通渠道。
遏制与轮换
- [ ] 已暂停继续使用旧凭证的任务或缩小其权限。
- [ ] 已检查凭证使用审计和相关资源变更。
- [ ] 已生成新凭证并存入受控秘密存储。
- [ ] 已在非生产环境验证新凭证。
- [ ] 已安排生产切换、观察窗口和回滚方案。
- [ ] 已撤销旧凭证,并确认没有其他项目依赖它。
清理与验证
- [ ] 已清理代码、历史引用、日志、缓存和制品中的敏感副本。
- [ ] 已重新扫描相关仓库和制品。
- [ ] 已验证普通开发者、非目标任务和低权限运行器无法读取该秘密。
- [ ] 已确认业务部署、监控、告警和审计功能正常。
- [ ] 已为误报、豁免和例外设置负责人及到期时间。
复盘与持续监控
- [ ] 已确定泄露根因,是误提交、日志回显、权限过宽还是制品打包错误。
- [ ] 已补充提交前检测、合并门禁和运行时扫描。
- [ ] 已将静态凭证替换为短期凭证或更小权限的身份。
- [ ] 已建立秘密访问、异常调用和公开仓库暴露告警。
- [ ] 已用演练验证禁用、轮换、回滚和沟通流程。
CI/CD 安全治理的目标不是保证凭证永远不会出现在错误位置,而是让凭证具备短生命周期、最小权限、可追踪使用和快速失效能力。发现问题后,先控制可用性,再清理传播路径;完成轮换后,再通过日志脱敏、提交前检测、权限收敛和持续监控降低复发概率,这样才能把一次凭证泄露事件转化为可验证的安全改进。

山东省烟台市 1F
密钥泄露后先禁用再清理,这个顺序很关键