重新审视 Cloudflare Workers 中的远程 Spectre 攻击

2026-08-20 28 预计阅读时间: 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.

预计阅读时间:12 分钟

Spectre 并没有因为攻击者无法直接登录服务器就失去意义。2024 年和 2025 年,Cloudflare 重新评估了针对 Workers 基础设施的远程 Spectre 攻击,并公开讨论了几个关键问题:攻击者如何构造可利用的 Spectre gadget,如何在远程环境中获得足够稳定的计时器,如何提高与目标租户共置的概率,以及新的防御措施如何进一步强化 Workers 的隔离边界。

这类研究的价值不在于提供一条简单的“远程打穿”路径,而在于提醒平台和租户:微架构侧信道的风险来自多个条件的组合。只要其中一个条件发生变化,例如计时精度提高、调度更可预测,或者攻击代码更容易与目标共享硬件,过去看似不可行的攻击假设就需要重新验证。

远程 Spectre 需要哪些攻击原语

Spectre 利用的是处理器推测执行与缓存状态之间的差异。攻击者通常不能直接读取受保护的数据,而是诱导处理器沿着错误的分支执行,再通过缓存访问时间推断某些秘密值。

在云端 Workers 场景中,攻击者需要把多个原语拼接起来:

  • Spectre gadget:目标代码中存在可被错误预测分支触发的访问模式,例如由攻击者控制索引、再根据索引访问敏感数组。
  • 远程计时器:攻击者需要从网络响应延迟、请求处理时间或其他可观测信号中提取微小差异。网络抖动会掩盖缓存侧信道,因此通常需要大量样本、统计聚合和噪声控制。
  • 共置机会:攻击代码和目标代码需要在足够接近的计算资源或处理器上下文中运行。调度策略、租户混合方式和请求路由都会影响成功概率。
  • 可重复的实验条件:单次异常延迟没有意义,攻击者需要让实验结果在多轮请求中保持统计显著。

这也解释了为什么“远程可达”不等于“远程容易利用”。攻击链上的每一环都可能降低信噪比,但平台评估不能只看单个环节是否困难,而要看攻击者是否能通过自动化请求、长时间采样和资源选择把这些困难叠加起来解决。

为什么远程计时器值得重新评估

传统浏览器会主动降低高精度计时器的可用性,但远程服务仍然可能泄露时间信息。攻击者可以比较一组请求的处理延迟,观察不同输入是否引起稳定的响应差异,再使用均值、分位数或假设检验降低网络噪声的影响。

下面是一个仅用于防御性实验的延迟采样脚本。它不会尝试读取任何秘密,也不会构造 Spectre gadget;它适合在自有服务上检查不同请求路径是否存在异常稳定的延迟差异。运行前把 TARGET 改成你有权测试的端点,并遵守服务的速率限制。

#!/usr/bin/env python3
import statistics
import time
import urllib.request

TARGET = "https://example.com/health"
SAMPLES = 30


def measure_once():
    started = time.perf_counter()
    request = urllib.request.Request(
        TARGET,
        headers={"User-Agent": "defensive-latency-check/1.0"},
    )
    with urllib.request.urlopen(request, timeout=5) as response:
        response.read(256)
    return (time.perf_counter() - started) * 1000


values = []
for _ in range(SAMPLES):
    try:
        values.append(measure_once())
    except Exception as exc:
        print(f"request failed: {exc}")
    time.sleep(0.2)

if values:
    print(f"count={len(values)}")
    print(f"median_ms={statistics.median(values):.2f}")
    print(f"p95_ms={statistics.quantiles(values, n=20)[18]:.2f}")
    print(f"min_ms={min(values):.2f} max_ms={max(values):.2f}")

这类脚本不能证明存在 Spectre 漏洞。延迟差异可能来自缓存、数据库、网络路由、垃圾回收、限流或冷启动。它的用途是帮助运营方发现“远程可观测信号”,然后结合运行时、调度器和硬件隔离信息做进一步分析。

Workers 隔离边界中的关键问题

对边缘运行时来说,隔离不仅是语言层面的变量作用域。还需要考虑以下边界:

  1. 不同租户的代码是否可能在同一个进程、线程或处理器核心上交错执行。
  2. 请求结束后,运行时是否复用相同的执行上下文和微架构状态。
  3. 调度器是否让攻击者通过重复请求提高与特定目标共置的概率。
  4. 运行时是否对高风险代码模式、计时信号和异常资源访问进行限制。
  5. 防御措施是否覆盖新出现的攻击原语,而不是只针对某一个已知 PoC。

对于平台提供方,这些问题需要在真实调度、真实噪声和长期采样条件下验证。对于 Workers 使用方,更实际的原则是:不要把“同一平台上的隔离”误当成所有层面的秘密隔离。高价值密钥、长期有效的令牌和高敏感度数据仍应尽量放在专门的密钥或服务边界之后,并减少它们进入通用请求处理路径的机会。

可以这样实践:减少敏感数据与不可信输入的交互

下面是一个简化的 Workers 示例。它展示的是应用层的降低暴露面做法:先验证请求,再调用受控的后端能力;敏感配置只在需要时读取,不把密钥放入响应、日志或返回给客户端。这里的代码不能替代平台级 Spectre 防御,但可以减少应用本身暴露的敏感状态。

部署前需要把 SECRET_BACKEND_URL 配置为自有后端,并通过 Wrangler Secret 设置 BACKEND_TOKEN

export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (request.method !== "POST" || url.pathname !== "/lookup") {
      return new Response("Not found", { status: 404 });
    }

    const contentType = request.headers.get("content-type") || "";
    if (!contentType.startsWith("application/json")) {
      return new Response("Unsupported media type", { status: 415 });
    }

    const body = await request.json();
    const query = typeof body.query === "string" ? body.query.trim() : "";
    if (query.length < 1 || query.length > 64) {
      return new Response("Invalid query", { status: 400 });
    }

    const upstream = await fetch(env.SECRET_BACKEND_URL, {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "authorization": `Bearer ${env.BACKEND_TOKEN}`,
      },
      body: JSON.stringify({ query }),
    });

    if (!upstream.ok) {
      return new Response("Upstream failure", { status: 502 });
    }

    const result = await upstream.json();
    return Response.json({
      ok: true,
      data: result.data,
    });
  },
};

配套的本地配置可以保持最小化:

name = "controlled-lookup"
main = "src/index.js"
compatibility_date = "2025-01-01"

[vars]
SECRET_BACKEND_URL = "https://internal.example.com/lookup"

设置密钥时不要把令牌写进仓库或普通变量文件:

npx wrangler secret put BACKEND_TOKEN
npx wrangler deploy

这里有几个边界需要明确:输入校验主要解决应用逻辑和资源滥用问题,并不能消除处理器侧信道;将密钥放到后端服务也不能自动保证绝对隔离,但可以缩短秘密在边缘请求处理过程中的生命周期,并减少不必要的暴露路径。

平台防御应覆盖整条攻击链

针对远程 Spectre,单一措施往往不够。平台防御可以围绕攻击链建立多层控制:

  • 在运行时和编译流程中识别或削弱高风险的推测执行 gadget。
  • 限制可用于建立远程计时器的高精度、低噪声信号。
  • 改进租户调度和资源隔离,降低攻击者主动获得共置条件的机会。
  • 对请求频率、异常重复采样和可疑资源访问建立检测机制。
  • 通过持续红队测试验证新防御是否仍能覆盖新的攻击原语。

其中,降低计时精度并不是万能方案。攻击者可能通过更多样本或更复杂的统计方法恢复信号,因此需要和调度隔离、代码防护及监控组合使用。同样,增加租户隔离也可能带来资源利用率、延迟和成本方面的取舍,平台需要根据威胁模型决定隔离强度。

采用建议

如果你负责 Workers 平台或在其上运行敏感业务,可以用下面的清单做一次复核:

  • 是否明确记录了租户、进程、线程和处理器层面的隔离假设?
  • 是否评估过网络延迟中可被长期采样的稳定信号?
  • 是否把高价值密钥从通用请求逻辑中移出?
  • 是否避免把秘密内容写入响应、异常信息和结构化日志?
  • 是否在平台升级、调度策略变化或运行时变化后重新做侧信道评估?
  • 是否把远程 Spectre 当作持续的工程风险,而不是只针对某个历史 PoC 的一次性修复?

重新审视远程 Spectre 的重点,是承认攻击能力会随着计时方法、共置策略和运行时防御的变化而变化。对 Cloudflare Workers 这类多租户边缘平台而言,可靠的安全边界需要由代码、运行时、调度器和硬件层共同构成,并通过持续验证来保持可信。


相关推荐