软件供应链风险已经从安全团队的专项议题,变成每个提交代码、引入依赖和发布版本的开发者都必须面对的工程问题。Omdia 的最新研究显示,过去一年有 77% 的组织经历过软件供应链事件。这个数字说明,攻击面并不只存在于生产环境,也存在于依赖包、构建脚本、CI/CD 流程和开发工具中。
风险不只来自业务代码
现代应用通常依赖大量第三方组件、开源库、基础镜像和自动化 Action。任何一个环节被篡改、配置不当或缺少验证,都可能影响最终交付物。
常见风险包括:
- 引入带有已知漏洞的依赖版本
- 下载到被攻击者接管或篡改的开源包
- 在 CI 配置中使用未固定版本的第三方 Action
- 构建过程缺少来源、完整性或权限校验
- 密钥、令牌和内部配置意外进入代码仓库或构建日志
- 生产环境无法追溯某个二进制文件由哪些源码和依赖生成
供应链安全的难点在于,开发者往往并没有直接看到攻击行为。一个看似普通的依赖升级,可能改变数百个传递依赖;一个便利的 CI 插件,也可能获得读取代码和访问凭据的权限。
为什么开发者是第一道防线
开发者最早接触依赖、构建配置和发布流程,因此能够在风险进入组织之前做出判断。安全工具可以发现问题,但工具的结果仍然需要有人决定是否升级、是否接受风险,以及如何修复兼容性问题。
有效的防线通常不是增加一层审批,而是把安全检查放进已有的开发路径:拉取请求检查依赖、构建时生成软件物料清单(SBOM)、发布前扫描镜像,并对高风险变更设置明确的阻断规则。
这也意味着团队需要把安全要求写成可执行的工程约束,而不是停留在培训材料或安全公告中。例如,依赖升级应当有锁文件和审查记录;构建任务应尽量使用最小权限;外部 Action 应固定到不可变的提交哈希,而不是跟随会随时变化的标签。
可以这样加固 CI 流程
下面是一个可以改造的 GitHub Actions 示例。它做三件事:固定第三方 Action 的版本、安装锁定的依赖、在构建前执行依赖审计。示例中的提交哈希只是占位符,实际使用时应替换为经过团队验证的完整 SHA,并定期评估升级。
name: supply-chain-check
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
security:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@<verified-commit-sha>
- name: Set up Python
uses: actions/setup-python@<verified-commit-sha>
with:
python-version: "3.12"
cache: pip
- name: Install locked dependencies
run: python -m pip install --require-hashes -r requirements.txt
- name: Audit dependencies
run: python -m pip install pip-audit && pip-audit
如果项目使用 Python 的哈希锁定方式,requirements.txt 可以类似这样配置:
requests==2.32.3 \\
--hash=sha256:<verified-wheel-hash> \\
--hash=sha256:<verified-sdist-hash>
运行前需要把占位符替换成真实且经过验证的值。对于不支持 --require-hashes 的现有项目,也可以先在 CI 中执行 pip-audit,再逐步引入锁定和哈希校验。关键是让依赖来源和变更记录可追溯,而不是一次性追求复杂配置。
团队还可以在本地或流水线中使用简单命令检查依赖:
python -m pip install --upgrade pip-audit
python -m pip freeze
pip-audit --strict
--strict 的行为应结合项目现状验证;如果构建环境中的工具无法识别某些包,先解决扫描覆盖率和误报问题,再把失败策略纳入合并门禁。
安全措施需要边界
供应链防护不能只依赖单个扫描器。漏洞数据库可能存在延迟,恶意包也可能在最初发布时没有明显漏洞特征。团队需要同时关注依赖来源、版本固定、构建权限、发布签名和产物追踪。
不过,安全策略也不能制造大量无法解释的阻断。一个不提供修复路径的告警会很快被开发者绕过。更实际的做法是按风险分级:对已确认的高危漏洞、来源不可信的组件和未经审查的发布流程进行阻断;对低风险或暂时无法升级的问题记录例外、指定负责人和截止日期。
给开发团队的落地清单
- 为直接依赖和传递依赖建立清单,并确保变更可审查
- 提交锁文件,避免构建结果随时间漂移
- 固定 CI Action、容器基础镜像和下载工具的版本
- 为构建任务配置最小权限,避免无必要地暴露生产凭据
- 在拉取请求和发布流程中执行依赖审计
- 生成并保存 SBOM,记录源码、依赖和构建产物之间的关系
- 为告警定义升级、例外和复查流程
- 定期演练撤回恶意依赖或重新构建受影响版本
77% 的事件发生率提醒组织重新审视软件供应链,但真正的改进发生在日常提交和发布动作中。开发者不需要独自承担全部安全责任,却必须成为安全控制能够落地的第一站:知道代码从哪里来、构建如何完成,以及最终产物为何值得被信任。