容器磁盘“删了还在”:Cloudflare 跨租户残留数据漏洞带来的隔离教训

2026-09-24 15 预计阅读时间: 1 分钟
来源: blog.cloudflare.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:9 分钟

外部安全研究团队 Accomplish 在 Cloudflare Containers 中发现了一项跨租户数据暴露漏洞:新工作负载可能接触到之前工作负载遗留在磁盘上的数据。Cloudflare 随后调查并修复了该问题。

这类漏洞不一定意味着攻击者突破了正在运行的容器。问题可能发生在更容易被忽视的生命周期边界上——旧实例已经退出,但它用过的存储又被分配给了另一个租户,而底层数据并未真正消失。

容器删除不等于数据消失

从控制平面看,删除容器通常只是结束进程、卸载文件系统并回收资源。但在存储层,以下操作含义完全不同:

  • 删除文件:通常只移除目录项或更新元数据。
  • 重新格式化:可能重建文件系统结构,却不覆盖所有数据块。
  • 释放卷或虚拟磁盘:只是把资源归还给池。
  • 覆盖底层块:主动改变原有数据,但在 SSD、稀疏文件和精简配置存储上仍可能受重映射影响。
  • 销毁独立加密密钥:在密钥隔离正确的前提下,使旧密文无法恢复。

因此,多租户容器平台不能把“工作负载已删除”直接当成“数据已清除”。只要底层磁盘、快照、缓存层或块设备会被复用,资源分配器就必须提供明确的清洁性保证。

根据给出的事件摘要,可以确认问题与前一工作负载的残留磁盘数据有关;摘要没有披露具体存储实现、利用路径或 Cloudflare 最终采用的内部清理机制,因此不应把某一种文件系统或磁盘技术当成此次事件的既定根因。

调查重点不是一块盘,而是整个复用链路

排查此类问题时,只修补容器启动脚本往往不够。工程团队需要沿资源生命周期建立证据链:

  1. 数据如何进入磁盘:应用层文件、临时目录、交换空间、日志、崩溃转储和镜像层都应纳入范围。
  2. 资源如何被释放:异常退出、超时强杀、宿主机重启和控制平面故障是否会绕过清理逻辑。
  3. 磁盘如何进入资源池:释放后是立即复用、延迟清理,还是先进入隔离区。
  4. 新租户能够看到什么:除了已挂载文件系统,还要考虑未分配空间、额外块设备和错误挂载的旧快照。
  5. 影响范围如何界定:哪些资源池、时间窗口、区域和实例类型可能经过同一条复用路径。

日志也需要谨慎设计。为了调查而记录完整文件内容,可能制造第二份敏感数据。更安全的做法是记录资源标识、状态转换、清理结果、不可逆摘要和时间戳,而不是客户明文。

用最小实验理解“逻辑删除”风险

下面的 Linux 脚本用普通文件模拟一块可复用磁盘。它不是 Cloudflare 实现的复现代码,而是一个可以本地运行的残留数据实验:写入标记后执行“逻辑释放”,原始字节仍然可以被扫描出来;覆盖后标记才消失。

运行前只需要 bash、truncate、dd 和 GNU grep,不要把真实密钥或客户数据写入测试标记。

#!/usr/bin/env bash
set -euo pipefail

DISK="$(mktemp)"
trap 'rm -f "$DISK"' EXIT

MARKER="tenant-a-canary-$(date +%s)-$$"
DISK_SIZE_MB=16
OFFSET=$((4 * 1024 * 1024))

truncate -s "${DISK_SIZE_MB}M" "$DISK"
printf '%s' "$MARKER" | dd \
  of="$DISK" bs=1 seek="$OFFSET" conv=notrunc status=none

echo "[1] Tenant A wrote a canary marker"

# 模拟只归还资源、不覆盖数据的释放过程。
true

if LC_ALL=C grep -aFq "$MARKER" "$DISK"; then
  echo "[2] Unsafe reuse detected: old bytes remain readable"
else
  echo "Unexpected result: marker was not found" >&2
  exit 1
fi

# 仅用于演示覆盖效果,不代表适用于所有 SSD 或云存储后端。
dd if=/dev/zero of="$DISK" bs=1M count="$DISK_SIZE_MB" \
  conv=notrunc status=none

if LC_ALL=C grep -aFq "$MARKER" "$DISK"; then
  echo "Sanitization failed: marker is still present" >&2
  exit 1
else
  echo "[3] Marker absent after sanitization"
fi

生产环境中的回归测试可以沿用“金丝雀”思路,但应通过平台自己的分配 API 完成:测试租户 A 写入随机标记并释放资源,测试租户 B 随后申请资源,再验证 B 无法读取该标记。测试标记必须是随机生成的无敏感数据,并且测试只能在授权环境中运行。

修复要同时覆盖存量、增量和失败路径

针对残留数据风险,可以这样设计分层防线:

  • 立即止血:暂停可疑资源池的跨租户复用,隔离尚未完成清理的磁盘。
  • 重新建立清洁状态:对可能受影响的存量资源执行经过验证的清理、加密擦除或销毁重建。
  • 改变分配协议:资源只有在清理完成并验证成功后,才能从隔离状态进入可分配状态。
  • 采用租户级加密边界:为租户或短生命周期工作负载使用独立数据密钥,释放资源时销毁密钥。密钥复用或隔离错误会削弱这一保证。
  • 默认失败关闭:清理任务超时、节点掉线或验证程序异常时,资源应继续隔离,而不是返回公共池。
  • 持续检测:把跨租户金丝雀测试、资源状态审计和异常复用告警加入发布门禁。

单纯执行 rm -rf 或快速格式化通常不足以证明安全;对 SSD 或精简配置存储反复写零也未必覆盖所有物理位置。平台应根据实际后端选择可验证的介质清理方法,或者通过正确实现的加密擦除降低对物理覆盖的依赖。

上线前应回答的几个问题

Cloudflare Containers 事件提醒平台团队:隔离不仅存在于 CPU、内存和网络命名空间,也存在于资源被释放和重新分配的那一刻。上线多租户容器能力前,至少应确认:

  • 新租户能否接触任何旧文件、未分配块、快照或交换数据?
  • 正常退出与异常退出是否经过同一套安全清理流程?
  • 清理失败时,资源是否自动进入隔离区?
  • 团队能否根据审计记录界定受影响的资源和时间窗口?
  • 存储清洁性是否有自动化回归测试,而不只是设计文档中的承诺?

真正可靠的边界不是“我们调用了删除命令”,而是“系统能够持续证明,上一位租户的数据不会交给下一位租户”。


相关推荐