首页 / 文章 / 你的 LLM API 密钥到底藏在哪?一篇文章讲清安全架构
← 返回
AI技术

你的 LLM API 密钥到底藏在哪?一篇文章讲清安全架构

✍️ zhirenhun 📅 2026/7/9 👁 49 阅读 ⏱ 27 分钟
你的 LLM API 密钥到底藏在哪?一篇文章讲清安全架构

你的 LLM API 密钥到底藏在哪?一篇文章讲清安全架构

如果有人今天攻破了你的某个项目依赖,他们能偷走你的 OpenAI、Anthropic 或 Gemini API 密钥吗?

答案不取决于你用的是哪家 LLM 供应商,也不取决于你的代码库有多安全。它主要取决于一个架构决策——一个很多团队从未认真思考过的决策:你的应用程序运行时,你的 API 密钥到底存放在哪里。

如果这个密钥存在于应用程序自己的进程里,那么该进程中的每一个依赖都共享着同一个环境。一个被攻破的包不需要"闯入"你的基础设施——它只是和你的应用程序以同等权限执行,可以访问你的代码能访问的所有凭据。

如果供应商密钥存放在一个独立的网络代理中,那么应用程序自始至终都不持有供应商凭据。即使某个依赖被攻破,攻击者也只能访问应用程序进程内存在的有限凭据。这并不能消除风险,但它可以在出问题时显著缩小爆炸半径。

在这篇文章中,我们将探讨两种主流的 LLM 网关架构,检视 API 密钥在每种设计中的具体存放位置,通过一个可复现的演示来展示两者在实际中的差异,并讨论为什么缩小爆炸半径往往比试图杜绝一切攻击更有价值。


LLM 网关架构:进程内 vs 网络代理

每一个 LLM 应用都有相同的基本任务:向模型供应商发送请求,并使用 API 密钥进行身份认证。

关键不在于应用是否使用 API 密钥,而在于发起请求时,这个密钥存在哪里。

目前,大多数 AI 应用遵循以下两种架构模式之一。

架构一:应用程序持有供应商密钥

进程内 LLM 网关架构示意图:供应商 API 密钥存储在应用程序进程内,与受感染的依赖相邻

供应商密钥存在于应用程序进程中,任何在 import 时运行的依赖都可能触及它

这是多数开发者已经熟悉的架构。

你的应用从环境变量中加载供应商 API 密钥,初始化 SDK 或网关库,然后直接向 OpenAI、Anthropic、Gemini 或其他供应商发起请求。

简化版本通常长这样:

api_key = os.environ["PROVIDER_API_KEY"]

client = OpenAI(api_key=api_key)

response = client.responses.create(...)

这种模式易于理解、实现迅速,对许多项目来说完全合理。应用程序拥有凭据,因为它就是直接与供应商对话的组件。

重要的细节在于:供应商密钥现在存在于应用程序的进程之中。在该进程中执行的每一个包、框架、插件和依赖都在同等权限下运行。如果其中一个依赖被攻破,供应商密钥就和恶意代码处在同一个环境中。

架构问题归结为:

如果应用程序进程内部某个组件被攻破,攻击者能从那里访问到哪些秘密?

架构二:应用程序与网络代理通信

网络代理 LLM 网关架构示意图:供应商 API 密钥隔离在独立的代理进程中,应用程序进程只持有一个范围受限的网关令牌

供应商密钥存在于独立的代理进程中。应用进程只持有一个作用域受限、可轮换的网关令牌

第二种模式将认证从应用程序中分离出来。

应用程序不再直接向模型供应商发送请求,而是发送给一个网关或代理。代理持有供应商 API 密钥,并代应用程序向上游发起请求。

从应用程序的视角来看,流程几乎一样:

Application
        │
        ▼
Gateway / Proxy
        │
        ▼
LLM Provider

区别在于应用程序没有的东西。

应用并不持有供应商凭据,而是持有一个作用域受限的网关令牌,用于授权通过代理的请求。代理验证该令牌,应用任何路由或策略决策,然后仅在代理自己的进程中注入供应商 API 密钥,再将请求转发到上游。

这改变了被攻破的后果。如果恶意代码在应用程序进程中执行,它仍然可以访问应用程序拥有的任何凭据。区别在于,供应商 API 密钥不再是其中之一。

这并不会让应用程序变得无懈可击。被盗的网关令牌仍然是一个安全事件。不过,与供应商 API 密钥不同,网关令牌的作用域可以精细控制、可以集中撤销、可以在不重新部署应用的情况下轮换,并且可以限制到特定的操作。

看到这种差异的最简单方式,是让同一个被攻破的依赖在两种架构下各运行一次。这正是我们接下来要做的。


供应链攻击如何暴露 LLM API 密钥

更重要的问题是:应用程序开始运行之后会发生什么。

进程一旦开始执行,它所需的凭据就对那个进程可用。如果你的应用可以读取 API 密钥,那么任何以同等权限执行的代码也同样可以读取它。

这正是供应链攻击如此有效的原因。

攻击者不再需要找到你应用程序中的漏洞。相反,他们在你的依赖树中攻破某个包,然后让你的应用代他们执行恶意载荷。在很多情况下,这些代码在安装或 import 阶段就已经运行了,远在你的业务逻辑启动之前。

import os

_INTERESTING = ("API_KEY", "SECRET", "TOKEN", "PASSWORD", "PRIVATE_KEY")

def harvest():
    found = {
        k: v
        for k, v in os.environ.items()
        if any(marker in k.upper() for marker in _INTERESTING)
    }

    for name, value in found.items():
        print(f"Found: {name}")

harvest()

这个示例刻意保持无害——它不发网络请求、不写文件、也不尝试外泄任何数据。它只是扫描当前进程中的凭据并打印出来。

重要的不是这段代码做了什么,而是这段代码在哪里运行。

想象这个包位于你的依赖关系图深处好几层。你不直接 import 它,也从未读过它的源代码。有一天,一个被攻破的版本进入你的 CI 管道,自动安装,并在正常的启动序列中执行。

如果你的应用程序在自己的环境中存储了供应商 API 密钥,依赖关系可以读取这个密钥,因为它在同一个进程中。

如果你的应用程序只持有一个作用域受限的网关令牌,而供应商凭据存于一个独立的代理进程中,那么完全相同的依赖关系仍然会成功执行,但供应商密钥根本就不在那里,无从发现。

这就是我们要探讨的架构区别。

这也是为什么 2026 年 3 月的 LiteLLM 供应链安全事件在整个 AI 生态系统中引起了如此大的关注。该事件之所以重要,不是因为 LiteLLM 格外脆弱,而是因为它展示了 AI 基础设施作为攻击目标的价值有多大,以及一个被攻破的依赖关系能多快地触及运行中应用程序内的高价值凭据。

在审视那个真实案例之前,值得先看看这个区别。

下面这个可复现的演示让同一个被攻破的依赖在两种架构下各运行一次。依赖关系本身没有任何变化,唯一的变量是供应商 API 密钥存放在哪里。


演示:进程内 vs 代理 LLM 网关安全性

理论很有用,但当你亲眼看到架构风险发生时,理解会深刻得多。

为了让这个对比具体化,我为此文准备了一个小型、无外部依赖的演示(由 Jonathan Hutchins 提供),它在两种架构下复现了完全相同的场景。

演示的设置故意保持简单:

应用程序本身从未改变。
同一个依赖在两个示例中都被 import。
唯一变化的是供应商 API 密钥存放在哪里

演示只使用 Python 标准库。没有外部服务、没有供应商账户、没有对 OpenAI 或 Anthropic 的网络调用、也没有真实的凭据。一切都在本地运行,可以轻松复现,无需担心副作用。

"恶意"依赖也同样简单。当它被 import 时,它会扫描当前进程中所有看起来像凭据的东西。

import os

_INTERESTING = (
    "API_KEY",
    "SECRET",
    "TOKEN",
    "PASSWORD",
    "PRIVATE_KEY",
)

def harvest():
    found = {
        k: v
        for k, v in os.environ.items()
        if any(marker in k.upper() for marker in _INTERESTING)
    }

    for name, value in found.items():
        shown = value[:8] + "..." if len(value) > 12 else value
        print(f"EXFILTRATED {name} = {shown}")

harvest()

场景 A:供应商密钥存在于应用程序内部

第一个版本遵循了当今许多 AI 应用使用的架构。

应用程序从自己的环境中读取供应商密钥:

api_key = os.environ["PROVIDER_API_KEY"]

当依赖被 import 时,它在完全相同的进程中运行。

结果,它立即发现了供应商凭据:

[malicious_dep@import]
EXFILTRATED PROVIDER_API_KEY = sk-provi...3xyz

这里并没有什么特别巧妙的手法。

依赖并没有绕过身份验证、利用内存破坏或闯入其他服务。它只是访问了已经存在于它执行所在的进程中的数据。

从攻击者的角度来看,这就够了。

场景 B:供应商密钥存在于网络代理中

现在让同一个依赖在第二种架构下运行。

这一次,应用程序从未收到供应商凭据。

相反,它只持有一个网关令牌:

token = os.environ["GATEWAY_TOKEN"]

供应商 API 密钥只存在于代理进程中,代理在将请求转发到上游之前会验证网关令牌。

当被攻破的依赖运行时,输出看起来截然不同:

[malicious_dep@import]
EXFILTRATED GATEWAY_TOKEN = gw-scope...-789

注意什么没有出现

没有供应商 API 密钥——因为它从一开始就不存在于应用程序的进程中。

应用程序仍然能收到成功的模型响应,但对 LLM 供应商的认证发生在代理内部,而不是应用本身。

看到这里,很容易得出结论说代理"解决"了问题。

并没有。

依赖仍然偷走了一个凭据。网关令牌是真实的,如果攻击者得到了它,他们可能仍然可以通过代理发起请求。假装并非如此会让这个对比失去意义。

问题不在于是否泄露了某些东西。而在于泄露了什么,该凭据能做什么,以及你能多快从这个泄露中恢复。

这正是两种架构开始产生重大分歧的地方。

演示的下一部分展示了网关令牌被盗后到底会发生什么,以及为什么恢复过程看起来与轮换一个受感染的供应商 API 密钥截然不同。


代理保护了什么,又没保护什么

在前面的例子中,被攻破的依赖还是偷走了一个凭据。

只不过它不是供应商 API 密钥。

相反,它获取了一个作用域受限的网关令牌,允许通过代理发起请求。这仍然是一个安全事件,提前承认这一点很重要。安全讨论在描述权衡时更加有用。

有趣的部分在攻破之后。演示的 rotate_demo.sh 脚本一步步走完了恢复过程。

最初,合法应用和攻击者都拥有同一个网关令牌,所以双方都可以成功使用它。这种临时的重叠是预期的,直到操作者撤销被攻破的凭据。

然后操作者更新了代理的令牌存储。

原始令牌被撤销。

一个新的作用域受限令牌被签发。

应用程序的代码没有任何改变。

没有任何东西被重新部署。

没有任何东西被重启。

代理只是开始拒绝被攻破的凭据,同时接受替换的凭据。

结果看起来像这样:

STEP 4  After rotation

[attacker (stolen v1)] BLOCKED -> HTTP 401
[app (v2)] ACCEPTED -> completion ok

演示的最后部分展示了网关令牌的另一个重要特性:作用域

令牌不代表对 LLM 供应商账户的无限制访问,它只在明确创建时指定的操作范围内有效。

如果同一个令牌在其允许的作用域之外被使用,代理会拒绝它。

STEP 5  Scoping

[app (v2, wrong scope)] BLOCKED -> HTTP 403

供应商 API 密钥通常是长期有效的,且授予对供应商账户的直接访问权限。如果它被攻破,轮换它通常意味着需要跨多个服务更新秘密、重新部署应用,并仔细协调变更以避免停机。

网关令牌代表的则是一个小得多的东西。它可以限制到单个应用、路由、团队或临时工作负载。如果它泄露了,操作者可以集中撤销它、签发替换令牌,并在不触及供应商凭据本身的情况下继续运行。这并不会让攻破变得无害,但使恢复更加简单。

随着 AI 系统变得越来越复杂,代理工作流依赖于许多库、插件、编排框架和 MCP 服务器,这种区别变得越来越重要。每增加一个组件,可信计算基础就会扩大,这使得缩小爆炸半径与完全防止故障同样重要。

当然,这不仅仅是理论探讨。2026 年 3 月,AI 生态系统目睹了一场真实的供应链攻破,正好说明了凭据存放位置为何重要。它不再要求开发者想象风险,而是提供了一个真实世界的例子,展示了被攻破的依赖能多快地演变成更大的安全事件。我们接下来就来审视这个事件。


2026 年 3 月 LiteLLM 供应链攻击详解

2026 年 3 月,LiteLLM——与多个 LLM 供应商交互时最广泛使用的网关之一——成为一场影响多个开源项目的更大规模软件供应链攻击的一部分。

根据 LiteLLM 自己的安全事后分析,攻击者在窃取了项目 CI 管道中的一个发布令牌后,成功向 PyPI 发布了两个被攻破的软件包版本(1.82.71.82.8)。攻破本身源于上游的一个恶意 GitHub Action,而不是 LiteLLM 应用代码中的漏洞——Datadog Security Labs 和 FutureSearch 也记录了这一细节。

LiteLLM 的事后分析估计恶意版本的发布窗口约为 40 分钟,而独立分析认为窗口更接近三小时。无论哪种情况,这段时间都足以让自动化 CI 管道安装被攻破的包。

恶意版本搜索了高价值凭据,包括云秘密、SSH 密钥、Kubernetes 令牌、数据库凭据和 API 密钥,然后试图将其外泄。LiteLLM 的事后分析列出了受影响凭据类型的详细信息,而 Datadog Security Labs 则分析了 payload 在执行时的行为。

最广为人知的下游受害者之一是 Mercor,该公司后来确认了一起与被攻破包相关的安全事件。这个案例展示了广泛使用的依赖中的攻破如何迅速传播到从未直接与原始攻击者交互的组织。

这里的教训不在于 LiteLLM 格外危险。攻破来自恶意 GitHub Action,而非 LiteLLM 的应用代码,而且项目反应迅速——发布了事后分析、重建了发布管道、并发布了一个干净版本(v1.83.0)。官方的 LiteLLM Proxy Docker 部署(固定了依赖版本)也未受影响,这进一步印证了依赖锁定、lockfile 和验证构建的价值。

最大的教训在于架构。

LLM 网关占据着独特敏感的位置,因为它们管理着解锁多个供应商访问权限的凭据。这些凭据存放在哪里,哪里就会成为攻破期间最有吸引力的目标。

这就是为什么问题不在于"我的某个依赖会不会被攻破?"

而是:

如果明天发生这种事,攻击者会在我的应用程序进程中发现哪些凭据?


那么,LLM API 密钥应该存放在哪里?

答案不是"始终放在代理后面"或"始终放在应用内部"。

正确的架构取决于你的团队运营需求、部署模型、性能要求和安全优先级。

本文希望表达清楚的是:你的供应商 API 密钥存放在哪里,直接决定了攻破的后果。

如果你的应用持有供应商密钥,任何以应用权限执行的代码都可能访问它。这并不会自动使架构不安全。大量生产系统成功地将进程内库与固定依赖、lockfile、隔离的 CI/CD 管道、秘密管理器和严格的网络控制结合使用。

如果你的应用与网络代理对话,供应商密钥就移到了一个独立的进程中。应用通常只持有一个作用域受限的网关令牌。如果恶意代码在应用中执行,攻击者仍然可以偷走那个令牌,但供应商账户本身位于应用的爆炸半径之外。恢复变成了撤销和轮换一个作用域受限凭据的问题,而不是在每一个依赖它的服务中替换供应商密钥。

两种架构都不能消除对依赖锁定、可重现构建、CI/CD 加固、最小权限访问和持续监控的需求。无论你的 API 密钥存放在哪里,这些实践都至关重要。架构只是决定了当这些防御失效时,攻击者能够触及什么。

如果你正在评估自己的架构,以下几个实际问题可以指导讨论:

应用程序运行时,供应商 API 密钥存在于哪里?
哪些进程可以访问该凭据?
如果我应用中的某个依赖今天被攻破,它能触及哪些秘密?
这些凭据能否独立于供应商账户进行作用域限制、撤销和轮换?
凭据泄露后,恢复需要多长时间?

如果你正在考虑基于代理的架构,目前有几种实现可供选择,包括官方的 LiteLLM Proxy 部署、放在模型供应商前面的云 API 网关,以及像 SteadIO 这样的代理原生解决方案。

SteadIO 是一个开源的自托管 LLM 网关,位于你的应用和模型供应商(如 OpenAI 和 Anthropic)之间。除了将供应商 API 密钥从应用进程中隔离出来,它还增加了若干运营能力,随着 AI 系统的发展,这些能力变得越来越有价值。

主要能力包括:

按代理和按团队的请求归属,便于了解哪些代理在产生流量。
实时 token 和成本追踪,使用供应商精确的定价。
预算执行,让团队在成本失控前自动停止 runaway 代理。
集中认证和网关令牌管理,使作用域受限凭据可以在不改变供应商 API 密钥的情况下签发、撤销和轮换。
统一监控 AI 流量的控制面板,因为每个请求都已经经过网关。

SteadIO 仪表盘示例:显示集中式成本归属和代理级 AI 支出

SteadIO 仪表盘示例,展示集中式成本归属和代理级 AI 支出。由于每个请求都经过网关,预算、使用量和归属等运营洞察与凭据隔离同时成为可能

最终,问题不在于 API 密钥本身。而在于设计能够优雅失效的系统。

因为无论你的安全计划有多成熟,漏洞总会出现、依赖总会被攻破、错误总会发生。当那一天到来时,最有价值的安全决策可能不是阻止了事件发生的那一个。

可能是那个将爆炸半径保持得足够小、让你能快速恢复的架构决策。


最后的话

没有哪种架构能阻止每一次供应链攻击或每一个被攻破的依赖。你能控制的是出问题时哪些凭据被暴露,以及你能多快恢复。

所以,在发布你的下一个 AI 应用之前,花点时间回答我们开头提出的问题:

你的 LLM API 密钥到底藏在哪?

答案对你的安全态势的影响,可能比选择哪个模型供应商或 SDK 更大。


本文由 Jonathan HutchinsSteadIO 创始人)共同撰写,他的技术见解和演示帮助塑造了本文探讨的许多架构概念。


感谢阅读!🙏🏻
希望本文对你有帮助 ✅
请点赞并关注获取更多内容 😍

Hadil Ben Abdallah 用 💙 制作

LinkedIn GitHub Twitter


原文出处:Where Do Your LLM API Keys Actually Live?

🧑‍💻

zhirenhun

一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

LLM API安全 密钥管理 AI安全
← 上一篇
别让你的 LLM 被攻破:提示注入防御实战指南
下一篇 →
AGENTS.md 手把手教程:一步步构建,看 AI 编码助手如何用起来

📌 相关推荐

结构化输出 vs 工具使用 vs 预填充:2026年从Claude获取JSON
2026/7/11
在AWS Bedrock上构建生产级AI代理——值得关注的架构与代码决策
2026/7/11
如何为你的AI代理构建评估框架(确保其在生产环境中不崩溃)
2026/7/11
← 返回文章列表