缓存投毒是什么
缓存投毒指共享缓存被写入不受信的内容——来自 fork 的 PR、低权限分支或被入侵的作业——后续流水线读取并复用它,轻则引入错误依赖,重则执行被篡改的编译产物,是供应链攻击的一条现实路径。除恶意写入外,也包括「键设计不当导致内容混入」的非恶意损坏。防法的核心:收紧谁能写缓存、把缓存当优化而非常识来源、关键产物在消费端做校验。
基本信息
| 适用平台 | GitLab CI、GitHub Actions、Jenkins、容器构建缓存均涉及 |
|---|---|
| 风险 | 恶意或错误内容写入缓存后被后续流水线复用 |
| 缓解 | 仅默认分支写缓存;fork/PR 只读;缓存与制品分离 |
| 验证 | 关键产物以校验和或签名在消费端复核 |
典型使用场景
- 开源仓库:fork PR 可以触发构建,但不应能写入默认分支的缓存
- 多租户 runner:不同项目共享执行机,本地缓存目录必须按项目隔离
- 公共依赖缓存:自建 npm/pip 镜像缓存被上游投毒后向下游扩散
- 容器构建缓存:BuildKit/Buildx 的缓存导入必须限制来源,避免载入被篡改的层
- 应急排查:产物行为异常时,回溯它命中的缓存条目与写入者
隔离写法示例
隔离写法示例
# GitLab:缓存键带分支 slug,天然隔离不同分支的写入
cache:
key: "$CI_COMMIT_REF_SLUG-pip"
paths:
- .cache/pip
# GitHub Actions:fork PR 的缓存默认只读,不能回写默认分支缓存
# 关键:产物不进缓存,走制品库并附校验和写入权限模型
| 来源 | 可读 | 可写 |
|---|---|---|
| 默认分支流水线 | 是 | 是(唯一可写来源) |
| 受保护分支 | 是 | 按策略,建议受限 |
| fork / 外部 PR | 仅读本 PR 缓存 | 否 |
| 本地开发机 | 可读 | 否(不共享生产缓存) |
把缓存当「优化」而不是「事实来源」:缓存缺失或损坏时构建仍应正确完成,只是变慢。
持久化工作目录也等同共享缓存:跑过不受信作业的 runner 若不清洗工作区,残留文件会混进下一次构建的输入,托管 runner 与自建 runner 都要把作业前清理写进流程。