☰
LiteLLM 网关权限缺陷剖析:三处漏洞如何串成提权链
2026/10/20 4:33:26 网站建设 项目流程

1. 从一次异常日志说起:为什么自建网关的权限问题比想象中更隐蔽

很多团队在内部搭建大模型统一调用层的时候,第一反应都是找一个开源的网关项目来快速落地。LiteLLM 就是这类场景里出现频率相当高的一个选择——它能把不同厂商的模型接口统一成一套 OpenAI 兼容的调用格式,省去了每个业务线各自对接的麻烦。但我在实际排查一次内部服务的异常日志时发现,这类自建网关的权限边界,往往比我们以为的要模糊得多。

事情的起因很简单:某天运维同事在巡检时发现,网关服务的访问日志里出现了一批来源不太对劲的请求。这些请求的路径看起来很正常,都是标准的模型调用接口,但请求头里携带的凭证信息却明显不属于任何一个已注册的业务方。更奇怪的是,这些请求最终都返回了 200,也就是说它们成功调用了后端模型。这就引出了一个很现实的问题:一个本该只对内部服务开放的网关,为什么会被外部请求"敲门"成功?

顺着这条线索往下查,问题逐渐清晰起来。LiteLLM 在权限校验的设计上,存在几处看起来各自独立、但组合起来却能形成完整提权路径的缺陷。单独看每一处,可能都觉得"影响不大",但把它们串起来,就变成了一条从匿名访问到越权调用的完整链路。这篇文章我就把这条链路的成因、复现思路和修复方案完整拆一遍,适合正在用 LiteLLM 做内部网关、或者准备自建类似统一调用层的同学参考。

需要先说明的是,下面涉及的所有环境、账号、请求示例都是我在测试环境里用模拟数据构造的,不涉及任何真实业务信息。重点在于理解这类权限缺陷的共性逻辑,而不是针对某个具体版本做攻击演示。

2. LiteLLM 的权限模型到底是怎么设计的

2.1 三层校验结构:主密钥、虚拟密钥与路由白名单

要理解这条提权链,得先搞清楚 LiteLLM 的权限校验是怎么分层的。它的设计思路其实不复杂,大致可以分成三层。

第一层是主密钥(Master Key)。这是部署时通过环境变量配置的一个高权限凭证,拥有对网关的完全控制权,包括创建、删除虚拟密钥,查看所有调用记录,修改模型路由配置等。主密钥通常只在运维层面使用,不应该出现在任何业务代码里。

第二层是虚拟密钥(Virtual Key)。这是给具体业务方分配的调用凭证,每个密钥可以绑定不同的权限范围,比如允许调用哪些模型、每分钟的请求上限、每月的 token 配额等。虚拟密钥是业务侧实际使用的凭证,理论上应该做到"最小权限"。

第三层是路由与模型白名单。即使持有有效的虚拟密钥,也不代表能调用所有模型。LiteLLM 支持在配置里限定某个密钥只能访问特定的模型列表,这层校验是在密钥验证通过之后才执行的。

正常情况下,一个请求要成功调用后端模型,需要依次通过这三层校验。问题就出在,这三层校验在某些特定条件下并不是严格串行执行的,存在可以绕过的缝隙。

2.2 校验顺序里藏着的缝隙

我在测试环境里反复构造请求后发现,LiteLLM 在处理某些特殊路径时,校验逻辑的顺序和常规调用路径并不一致。常规的模型调用请求会完整走完三层校验,但一些辅助性的管理接口或者兼容性接口,在实现上可能只做了部分校验。

举个具体的例子:某些用于查询模型列表、获取服务状态的接口,设计初衷是"只读且无敏感信息",所以在实现时可能只校验了请求是否携带了任意一个格式合法的密钥,而没有严格验证这个密钥是否真实存在、是否有对应权限。这类接口单独看确实不敏感,但如果它能返回一些内部结构信息,比如可用的模型名称、路由配置的片段,那就为后续的越权调用提供了情报基础。

这就是第一处缺陷的核心:部分接口的校验强度低于主调用路径,形成了信息泄露的入口。很多人部署时会默认所有接口的权限校验是一致的,但实际上不同接口的实现质量参差不齐。

2.3 为什么"看起来没问题"的配置实际上有风险

这里要特别提醒一点:很多团队在部署 LiteLLM 时,会按照官方文档的最小配置快速跑起来,然后就直接接入业务了。这种配置在功能上没问题,模型能调通,业务能跑起来,但从权限角度看,它可能处于一个"默认宽松"的状态。

比如,如果没有显式配置模型白名单,某些版本的默认行为是允许访问所有已注册模型;如果没有关闭某些调试接口,它们可能在生产环境依然可访问;如果主密钥和虚拟密钥的权限边界没有在配置层面做硬隔离,那么一旦虚拟密钥泄露,攻击者能做的事情可能远超预期。

我在排查时的一个深刻体会是:自建网关的安全水位,往往取决于部署时有没有人认真想过"最坏情况下会怎样",而不是取决于用了哪个开源项目。LiteLLM 本身提供了不少安全配置项,但默认配置和加固配置之间的差距,需要部署者自己填上。

3. 三处缺陷是怎么串成一条提权链的

3.1 第一处:密钥格式校验与密钥有效性校验的分离

第一处缺陷我在前面已经提到了,核心是格式校验和有效性校验被拆开了。某些接口在收到请求时,只检查了 Authorization 头里有没有携带一个符合特定格式的字符串,比如是否以sk-开头、长度是否在合理范围内,但没有真正去数据库或缓存里查这个密钥是否存在、是否被禁用。

这个缺陷单独利用的价值有限,因为攻击者拿不到任何实质性的模型调用能力。但它能起到两个作用:一是确认网关的存在和版本特征,二是通过返回信息的差异,判断哪些路径的校验更弱。这就像小偷踩点时先试着推几扇门,看看哪扇门只是虚掩着。

我在测试时构造了一个格式合法但完全不存在的密钥,去请求几个不同的接口。结果发现,模型调用接口正确返回了 401,但某个状态查询接口却返回了 200,并且响应体里包含了当前网关已加载的模型列表。这个列表本身不算机密,但它告诉了攻击者:这个网关上有哪些模型可用,接下来该往哪个方向尝试。

3.2 第二处:虚拟密钥的模型白名单在特定路径下未生效

第二处缺陷更关键一些。LiteLLM 支持给虚拟密钥配置模型白名单,比如某个密钥只能调用gpt-4和claude-3,不能调用其他模型。这个白名单在标准的/v1/chat/completions路径下是生效的,我实测过,用受限密钥去调用白名单外的模型会被正确拒绝。

但问题在于,LiteLLM 为了兼容不同厂商的接口风格,实现了一些额外的路由别名或者兼容路径。在这些路径下,模型白名单的校验逻辑可能没有被正确挂载。也就是说,同一个受限密钥,走标准路径调不通的模型,换一个兼容路径可能就调通了。

这个缺陷的隐蔽性很强,因为从业务角度看,这些兼容路径本来就是给特定场景用的,平时可能没人会去测"用受限密钥走兼容路径调白名单外的模型会怎样"。但攻击者一旦通过第一处缺陷拿到了模型列表,再结合对 LiteLLM 路由结构的了解,就很容易找到这些校验薄弱的路径。

我在这里踩过的一个坑是:一开始我以为只要在配置里写死了模型白名单就万事大吉,后来才发现白名单的生效范围是跟具体路由绑定的,不是全局强制的。配置项写了不等于所有路径都生效,这是自建网关时特别容易忽略的一点。

3.3 第三处:管理接口的权限继承逻辑存在缺陷

第三处缺陷是整条链路的"放大器"。LiteLLM 提供了一些管理类接口,用于动态创建密钥、修改配额、查看调用统计等。这些接口理论上应该只允许主密钥访问。但在某些实现里,权限判断的逻辑是"如果请求携带的密钥具有管理标志位,则允许访问",而管理标志位的设置又依赖于密钥创建时的参数。

问题来了:如果第二处缺陷让攻击者能够以某种方式调用到密钥创建相关的内部逻辑,或者如果存在某个接口能够修改现有密钥的属性,那么攻击者就可能把一个普通虚拟密钥"升级"成具有管理权限的密钥。一旦拿到管理权限,就可以创建新的高权限密钥、修改模型路由、甚至读取其他业务的调用记录。

把这三处串起来看,完整的链路是这样的:先用第一处缺陷确认网关信息和可用模型,再用第二处缺陷绕过模型白名单调用到本不该访问的模型,最后尝试用第三处缺陷把普通密钥提权为管理密钥。每一步单独看都不是"致命漏洞",但组合起来就形成了从匿名到完全控制的完整路径。

缺陷编号缺陷位置单独影响在链路中的作用
第一处部分接口只校验密钥格式信息泄露踩点,获取模型列表和版本特征
第二处兼容路径下白名单未生效越权调用绕过模型限制,扩大可访问范围
第三处管理接口权限继承逻辑缺陷权限提升将普通密钥升级为管理密钥

4. 在测试环境里复现这条链路的完整过程

4.1 环境搭建与初始配置

为了把这条链路彻底搞清楚,我在本地测试环境里搭了一套 LiteLLM 服务。配置上我故意采用了比较常见的"快速上手"方式:只设置了主密钥,创建了一个受限的虚拟密钥,给这个密钥配置了只允许访问某一个模型的白名单,其他都保持默认。

搭建过程本身没什么特别的,用官方的容器镜像或者 pip 安装都可以。我选择的是 pip 安装方式,方便直接看源码里的校验逻辑。启动命令里指定了主密钥和配置文件路径,配置文件里注册了两个模拟的模型后端,一个标记为"允许",一个标记为"禁止"。

这里有个细节值得说一下:我在配置文件里显式写了模型白名单,但后来发现白名单的字段名和生效范围在不同版本里有差异。有的版本是在密钥创建时通过参数指定,有的版本是在配置文件里针对路由指定。如果你也在用 LiteLLM,建议先确认自己版本里白名单到底是怎么生效的,不要想当然。

4.2 第一步:用伪造密钥探测接口校验强度

环境跑起来之后,我做的第一件事是构造一个格式合法但不存在的密钥,去请求几个不同的接口。具体做法很简单,就是把 Authorization 头设置成Bearer sk-加上一串随机字符,长度和真实密钥保持一致。

请求标准模型调用接口时,返回的是 401,提示密钥无效,这个符合预期。但请求某个状态查询接口时,返回的却是 200,响应体里列出了当前网关加载的所有模型名称。我又试了几个其他辅助接口,发现有的返回 403,有的返回 200,校验强度确实不一致。

这一步的收获是:确认了第一处缺陷真实存在,并且拿到了可用模型列表。从攻击者视角看,这就完成了踩点。从防守者视角看,这说明部署时需要对所有对外暴露的接口做一遍权限校验审计,不能只盯着主调用路径。

4.3 第二步:用受限密钥走兼容路径调用白名单外模型

拿到模型列表后,下一步是尝试用那个受限虚拟密钥去调用白名单外的模型。我先走标准路径,果然被拒绝了,返回信息明确提示该密钥无权访问目标模型。

然后我换了一个兼容路径。LiteLLM 为了适配不同厂商的接口风格,实现了一些路径别名。我逐个尝试,发现其中一个别名路径下,同样的密钥、同样的目标模型,请求竟然成功了。响应体里正常返回了模型输出,说明后端确实被调用了。

这个结果说明第二处缺陷也真实存在。我在这里做的额外验证是:换不同的模型、不同的密钥组合反复测试,确认这不是偶发问题,而是兼容路径下白名单校验确实缺失。这个缺陷的修复不能只靠改配置,需要在代码层面把白名单校验挂到所有路由上。

4.4 第三步:尝试通过管理接口提升密钥权限

第三步是最关键的,也是我在测试时最谨慎的一步。我尝试用那个受限密钥去访问管理类接口,看能否修改自身属性或者创建新密钥。直接访问创建密钥的接口时,返回的是 403,说明基础的管理权限校验是有的。

但我注意到,有一个用于更新密钥元信息的接口,它的权限判断逻辑似乎只检查了"密钥是否存在",而没有严格检查"当前密钥是否有权修改目标密钥"。我构造了一个请求,试图用受限密钥去修改自己的权限标志位。第一次尝试失败了,返回参数错误。调整参数格式后再次尝试,这次接口返回了 200,但实际权限并没有变化——说明这个接口虽然有校验缺陷,但还没到能直接提权的程度。

不过这个测试过程让我确认了第三处缺陷的存在:管理接口的权限继承逻辑确实不够严谨,存在被进一步利用的可能。在真实攻击场景里,攻击者可能会结合其他信息泄露或者参数构造技巧,把这一步走通。我在测试环境里没有继续深入,因为目的已经达到了——证明这条链路在逻辑上是成立的。

提示:以上复现过程全部在隔离的测试环境中完成,使用的都是模拟数据和模拟模型后端。实际生产环境中,任何未经授权的权限测试都可能违反服务条款甚至法律法规,请务必在获得明确授权的范围内进行安全评估。

5. 修复方案:从配置加固到代码层补丁

5.1 立即可做的配置层加固

发现这些问题之后,我首先做的是配置层的加固,因为这部分不需要改代码,见效最快。

第一件事是关闭所有非必要的接口。LiteLLM 的配置里通常有开关可以控制哪些接口对外暴露。我把状态查询、模型列表这类辅助接口全部限制为只允许内网访问,或者直接关闭。对于必须保留的,加上额外的认证层。

第二件事是显式配置模型白名单并验证生效范围。不能只在密钥创建时指定白名单就完事,还要在配置文件的路由层面再做一层限制,确保即使某个路径的密钥校验被绕过,路由层的白名单依然能拦住。我在配置里把每个模型的路由都绑定了允许访问的密钥列表,做了双重保险。

第三件事是分离主密钥和虚拟密钥的使用场景。主密钥只用于运维管理,绝对不下发到业务侧。虚拟密钥的创建和修改全部通过独立的运维流程完成,业务侧只能使用,不能管理。这样即使虚拟密钥泄露,攻击者能做的也有限。

加固项具体操作预期效果
关闭非必要接口在配置中禁用状态查询、模型列表等辅助接口消除信息泄露入口
双重白名单密钥级 + 路由级同时配置模型白名单防止单层校验被绕过
密钥分离主密钥仅运维使用,业务只用虚拟密钥限制泄露后的影响范围
访问日志审计开启详细日志,监控异常密钥请求及时发现探测行为

5.2 需要改代码才能堵住的漏洞

配置层加固能挡住大部分简单利用,但要彻底修复,还是得在代码层面动手。我梳理了一下需要改的地方。

针对第一处缺陷,需要在所有接口的入口处统一加上密钥有效性校验,不能有的接口只查格式、有的接口查有效性。最稳妥的做法是抽一个公共的认证中间件,所有路由都必须经过它。这样就不会出现"某个接口忘了加校验"的情况。

针对第二处缺陷,需要把模型白名单的校验逻辑从具体的路由处理函数里抽出来,放到路由匹配之后的统一拦截层。不管请求走的是标准路径还是兼容路径,只要匹配到了某个模型,就必须经过白名单校验。这样才能保证校验的覆盖面。

针对第三处缺陷,需要重新设计管理接口的权限判断逻辑。不能只判断"密钥是否存在",还要判断"当前密钥是否有权对目标资源执行该操作"。这涉及到权限模型的细化,可能需要引入基于角色的访问控制,把"管理密钥"和"普通密钥"的权限边界在数据层面就区分开。

我在测试环境里试着改了认证中间件这部分,把密钥有效性校验统一收口之后,第一处缺陷导致的 200 响应就消失了,所有伪造密钥的请求都正确返回 401。这个改动不算大,但效果立竿见影。

5.3 加固后的验证清单

改完之后怎么确认真的修好了?我整理了一个验证清单,每次改动后都跑一遍。

  • 用伪造密钥请求所有对外接口,确认全部返回 401,没有任何接口泄露信息。
  • 用受限密钥走所有已知路径(包括标准路径和兼容路径)调用白名单外模型,确认全部被拒绝。
  • 用受限密钥尝试访问所有管理接口,确认全部返回 403。
  • 检查日志,确认所有异常请求都被记录,且记录里包含足够的排查信息。
  • 用正常密钥走正常路径调用正常模型,确认业务功能不受影响。

这个清单看起来简单,但实际跑下来能发现不少遗漏。我第一次改完后就发现有一个兼容路径忘了覆盖,用受限密钥还是能调通白名单外的模型。补上之后才彻底堵住。

6. 这类权限缺陷的共性规律与日常防守思路

6.1 为什么"功能优先"的开源项目容易出这类问题

把这三处缺陷放在一起看,会发现它们有一个共同的根源:功能实现优先于权限设计。LiteLLM 作为一个网关项目,核心目标是让不同厂商的模型接口能统一调用,权限系统是后来加上去的。这就导致权限校验的逻辑是"哪里需要哪里加",而不是"从入口统一拦截"。

这种模式在开源项目里很常见。项目初期为了快速支持各种功能,会在不同的路由处理函数里各自实现校验。随着功能越来越多,校验逻辑就散落在各处,有的地方严、有的地方松,有的地方甚至忘了加。这不是 LiteLLM 独有的问题,很多自建网关、API 聚合层都有类似的隐患。

我在排查过程中最大的体会是:评估一个自建网关的安全性,不能只看它有没有权限功能,还要看权限校验是不是统一收口的。如果校验逻辑散落在各个处理函数里,那基本上可以断定存在覆盖不全的地方。

6.2 自建网关部署时最容易忽略的三件事

结合这次排查,我总结了自建网关部署时最容易忽略的三件事,每一件都对应着上面的一处缺陷。

第一件是接口清单审计。很多人部署完只测主调用路径,觉得能调通模型就完事了。但实际上网关暴露的接口远不止模型调用这一个,还有状态查询、模型列表、密钥管理、日志查看等等。这些接口的权限校验强度参差不齐,必须逐个审计。

第二件是路径覆盖测试。同一个功能可能有多个路径入口,标准路径校验严,兼容路径可能就松。部署后应该把所有已知路径都测一遍,确认权限校验在所有路径上都生效。

第三件是权限边界验证。不要假设"配置里写了就生效",要实际验证。用低权限凭证去尝试高权限操作,看是否被正确拒绝。这个验证应该定期做,因为项目升级后权限逻辑可能变化。

6.3 把权限校验收口到统一入口的实践建议

如果让我给正在用 LiteLLM 或者类似网关项目的团队一个建议,我会说:尽早把权限校验收口到统一入口。

具体做法是,在网关的请求处理流程里,找一个所有请求都必经的环节,把认证和授权逻辑放在那里。认证负责确认"你是谁",授权负责确认"你能做什么"。所有路由在处理业务逻辑之前,都必须先经过这个环节。这样就不会出现某个路由忘了加校验的情况。

如果项目本身的结构不支持这样做,那至少要做到两点:一是维护一份完整的接口清单,每个接口的权限要求都写清楚;二是每次升级或改动后,都跑一遍权限验证清单。这两点看起来笨,但确实能挡住大部分问题。

我在测试环境里把认证逻辑收口之后,再去做之前那些探测,伪造密钥全部被拦,受限密钥走兼容路径也被拦。这说明收口这个方向是对的。当然,收口之后还要持续维护,因为新加的功能如果绕过了统一入口,问题又会回来。

最后分享一个我在实际操作中养成的习惯:每次给网关加新功能或者升级版本后,我都会用之前那套探测脚本再跑一遍,看看有没有新的接口暴露出来、有没有路径的校验变松了。这个习惯帮我提前发现过好几次配置漂移的问题。自建网关的安全不是一次性的工作,而是需要持续盯着的日常。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询