镜像供应链安全威胁模型:常见攻击向量与威胁建模方法

镜像供应链安全的核心挑战在于:从开发者提交代码到镜像在生产环境运行的整个链条中,每一个环节都可能成为攻击者的突破口。理解这些攻击向量并掌握系统化的威胁建模方法,是构建有效防护策略的前提。

2.1 镜像供应链的四大攻击向量

攻击者通常利用镜像构建、分发、部署过程中的信任盲区实施攻击。以下按攻击阶段分类,列出最常见的四种攻击向量:

攻击向量 攻击阶段 典型手法 影响后果
镜像投毒 构建 / 拉取 在基础镜像中植入后门、挖矿程序或 Rootkit;通过恶意 Dockerfile 指令(如 RUN curl)下载恶意负载 容器运行时被远程控制、数据泄露、资源被滥用
镜像篡改 存储 / 传输 中间人攻击篡改 Registry 中的镜像层;利用未签名的 Manifest 替换合法镜像标签 拉取到被篡改的镜像,导致生产环境运行非预期代码
依赖混淆 构建阶段 攻击者向公共包管理器上传与内部私有包同名的恶意包(如 npm、pip、maven),利用包解析优先级差异自动引入 构建过程静默引入恶意代码,污染最终镜像
供应链后门 上游依赖 攻陷上游开源项目或基础镜像维护者账号,在合法版本中植入隐蔽后门(如 SolarWinds 式攻击) 下游所有使用该依赖的组织同时受害,检测难度极高
⚠️ 关键认知: 上述四种攻击向量并非孤立存在。一次成功的供应链攻击往往组合使用多个向量——例如先通过依赖混淆在构建阶段投毒,再通过篡改 Registry 中的签名信息掩盖痕迹。威胁建模时必须考虑攻击链的串联效应。

2.2 镜像投毒与篡改的深层机制

镜像投毒和篡改是容器供应链中最直接的攻击方式。理解其技术细节有助于设计针对性的检测策略。

  • 投毒入口点:
    • 基础镜像层: 使用官方但已过时的镜像(如 node:14 而非 node:20-slim),其中可能包含已知 CVE 漏洞。
    • 构建时注入: Dockerfile 中的 ADD 或 COPY 指令从不受信任的 URL 获取文件;RUN 指令执行未经验证的脚本。
    • 多阶段构建残留: 构建阶段使用的工具链(如编译器、调试工具)被植入后门,最终阶段未清理干净。
  • 篡改攻击面:
    • Registry 未启用 TLS: 明文传输时攻击者可通过 ARP 欺骗或 DNS 劫持替换镜像层数据。
    • 镜像签名缺失: 未使用 cosign 或 notation 对镜像进行数字签名,导致客户端无法验证镜像完整性。
    • 标签可变性: 使用可变标签(如 :latest)而非不可变摘要(如 @sha256:...),攻击者可重新打标签指向恶意镜像。
# 示例:使用 cosign 验证镜像签名(防止篡改)
cosign verify \
  --key cosign.pub \
  registry.example.com/my-app:v1.0.0

# 输出示例(验证通过时):
# Verification for registry.example.com/my-app:v1.0.0 --
# The following checks were performed on each of these signatures:
#   - The cosign claims were validated
#   - The signatures were verified against the specified public key

2.3 依赖混淆攻击的完整攻击链

依赖混淆攻击近年来在容器镜像供应链中愈发频繁。其核心是利用包管理器的解析优先级漏洞,让开发者无意中安装恶意包。

🔍 攻击链拆解(以 Python 生态为例):
  1. 侦察: 攻击者通过公开代码仓库或内部文档,获取目标组织使用的私有包名(如 internal-auth-lib)。
  2. 注册: 在 PyPI 上注册同名包 internal-auth-lib,版本号高于内部私有仓库中的版本(如内部为 1.0.0,攻击者上传 99.0.0)。
  3. 触发: 开发者在 requirements.txt 中未指定 --index-url 或未配置私有源优先级,pip install 默认从 PyPI 拉取最高版本。
  4. 执行: 恶意包的 setup.py 或 __init__.py 在安装时执行恶意代码(如反弹 Shell、窃取环境变量中的密钥)。
  5. 持久化: 恶意代码被写入镜像层,随容器部署进入生产环境,长期潜伏。

防御依赖混淆的关键措施包括:

  • 在包管理配置中明确指定私有源优先级(如 pip.conf 中的 extra-index-url 与 trusted-host)。
  • 使用 pip freeze 锁定所有依赖的精确版本和哈希值(--require-hashes)。
  • 在 CI/CD 流水线中集成依赖扫描工具(如 pip-audit、safety),自动检测已知恶意包。

2.4 威胁建模方法:从 STRIDE 到容器专用框架

系统化的威胁建模能够帮助团队在镜像供应链的每个环节提前识别风险。以下介绍两种适用于容器环境的建模方法。

方法 核心维度 在镜像供应链中的应用
STRIDE Spoofing(假冒)、Tampering(篡改)、Repudiation(抵赖)、Information Disclosure(信息泄露)、Denial of Service(拒绝服务)、Elevation of Privilege(权限提升) 逐维度分析镜像构建、签名、存储、拉取、部署环节。例如:Tampering 对应镜像篡改;Spoofing 对应依赖混淆中的包名假冒。
攻击树(Attack Tree) 根节点为攻击目标,子节点为达成目标所需的具体步骤 以“在容器中执行恶意代码”为根节点,分解出“投毒基础镜像”“篡改 Registry 中的镜像层”“利用依赖混淆引入恶意包”等分支。
容器威胁矩阵 结合 MITRE ATT&CK 框架,针对容器环境的 14 个战术阶段 覆盖从初始访问(如通过恶意镜像)到持久化(如挂载宿主机目录)的完整攻击生命周期。适用于与 Kyverno 策略映射。
💡 实践建议: 在 DevSecOps 团队中,建议将 STRIDE 与攻击树结合使用:先用 STRIDE 做全维度覆盖,再用攻击树对高风险路径做深度拆解。每次镜像构建流程变更(如更换基础镜像、新增依赖源)时,都应重新执行威胁建模。

2.5 镜像供应链威胁模型全景图

下图将上述攻击向量与威胁建模方法整合为一个全景视图,展示从代码提交到容器运行时的完整威胁面:

镜像供应链威胁模型全景图 镜像供应链威胁模型全景图 构建阶段 代码提交 依赖混淆 镜像投毒 Dockerfile 分发阶段 镜像签名 镜像篡改 Registry 标签可变 部署阶段 策略校验 运行时 后门执行 数据泄露 威胁建模方法覆盖 STRIDE 攻击树 容器威胁矩阵 Kyverno 策略 红色节点 = 攻击向量 / 风险点;蓝色节点 = 防御/控制点;绿色节点 = 运行时状态

2.6 威胁建模与 Kyverno 策略的映射关系

威胁建模的最终目的是指导策略的制定。下表展示如何将识别出的威胁映射为 Kyverno 可执行的策略规则:

威胁建模输出(风险项) Kyverno 策略类型 策略示例(简化)
镜像投毒:使用含高危 CVE 的基础镜像 verifyImages + validate
validate:
  message: "禁止使用含高危 CVE 的镜像"
  pattern:
    spec:
      containers:
      - image: "!*vuln-image*"
镜像篡改:镜像未签名或签名验证失败 verifyImages
verifyImages:
  imageReferences:
  - "registry.example.com/*"
  attestors:
  - count: 1
    entries:
    - keys:
        publicKeys: "-----BEGIN PUBLIC KEY-----..."
依赖混淆:构建时从不可信源拉取依赖 validate + mutate
mutate:
  patchStrategicMerge:
    spec:
      containers:
      - image: "*"
        env:
        - name: PIP_INDEX_URL
          value: "https://private-pypi.example.com/simple"
供应链后门:镜像层包含未知文件 validate + 外部扫描集成
validate:
  message: "镜像层文件列表与基线不符"
  deny:
    conditions:
      any:
      - key: "{{ images[].layers[] }}"
        operator: AnyIn
        value:
        - "/tmp/malicious.sh"
📌 核心原则: 威胁建模不是一次性的文档工作,而是持续迭代的工程实践。每次镜像供应链发生变更(如更换基础镜像、新增外部依赖源、修改构建流程),都应重新执行威胁建模,并同步更新 Kyverno 策略集。建议将威胁模型存储在 Git 仓库中,与策略代码一同进行版本管理和评审。