graphql-core:十万行注释,为什么一条都没算进max_tokens"

admin 2026-11-01 02:02:51 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

graphql-core:十万行注释,为什么一条都没算进 max\_tokens

原创

云梦DC
云梦DC

云梦安全

2026年10月29日 11:21
河南

去阅读

2026 年 10 月 22 日,graphql-core 披露了 CVE-2026-75508(GHSA-r77w-qph3-7vf8,CVSS 7.5),3.2.12 和 3.3.0rc1 修复。问题卡在 max_tokens 的计数口径上:它限制的是词法分析器交给语法层的有效 token,注释虽然被跳过,却照样创建对象、挂在链表上。

本地构造的一份查询大约 390 KB,真正的语法 token 只有 5 个,中间塞了 10 万条 # c 注释。3.2.8 在 max_tokens=5 下接受了它,解析完成后,loc 链表里有 100005 个 Token 对象。

漏洞速览

项目 内容
漏洞编号 CVE-2026-75508 / GHSA-r77w-qph3-7vf8
影响组件 graphql-core 3.2.3–3.2.11、3.3.0a3–3.3.0beta
修复版本 3.2.12、3.3.0rc1
漏洞类型 资源耗尽(CWE-400/770)
危害等级 CVSS 7.5 High
触发前提 服务端调用 parse() 时启用了 max_tokens

注释没有参与语法,但参与了分配

GraphQL 词法分析器在推进到下一个有效 token 时,会跳过空白和注释。调用方看到的是 advance_lexer() 返回了一段有效内容,max_tokens 的计数也跟着增加;注释不返回 token,自然没有计数。

解析器同时还要保留位置信息,方便报错时给出行列号。为此,扫描到的词法单元都会变成 Token 对象,通过 prev / next 组成双向链表,挂在文档的 loc 上。注释也走这条路径:先分配、记录位置、接进链表,然后才被跳过。

结果就是两个数字对不上。max_tokens 数的是语法层拿到的 token,内存里留下的是扫描过的全部 token。攻击者只要把大量内容写成注释,配额看到的仍然是一份很小的查询。

四组输入的对照

实验把 3.2.8 和 3.2.12 装进两个目录,用同一份脚本跑四组输入。

第一组是 { a },max_tokens=5,两版都通过,链表里是 5 个 token。第二组放 6 个真实 token,两版都抛 GraphQLSyntaxError。配额对普通 token 是有效的,这也说明为什么只做常规查询测试不容易碰到这个问题。

第三组是核心用例:10 万条注释后接 { a },max_tokens=5。3.2.8 接受,耗时 172 毫秒,顺着 loc 走链表可以数出 100005 个 Token。3.2.12 在相同输入上抛错,耗时 177 毫秒,拒绝发生在配额检查阶段。

第四组把注释加到 20 万条,源文本 9.7 MB。3.2.8 仍然接受,tracemalloc 峰值 55.3 MB;3.2.12 拒绝。这里要注意,修复版拒绝时也会先扫描并分配一部分 token,所以它的峰值不一定更低。修复解决的是“上限能不能生效”,不是“拒绝过程不花内存”。

深度限制和白名单为什么帮不上

深度限制、复杂度分析和查询白名单都要先拿到 AST,而 AST 是 parse() 成功之后才产生的。等这些规则开始工作,10 万个注释 token 已经分配完。

请求体大小限制理论上更靠前,但这类内容的压缩率很高。重复的 # c\ 被打包后可能只有几十 KB。如果大小检查放在反向代理上,看的又是压缩后的请求体,它看到的会是一份很普通的请求;等框架解压完再交给解析器,体积才展开。

所以缺口落在“解压之后、解析之前”。这个位置需要在应用侧补,单靠 GraphQL 的查询规则补不上。

升级后哪些请求会变

3.2.12 调整了 advance_lexer() 的计数,把两次有效 token 之间跳过的注释也算进去,并且覆盖到 EOF 前的最后一批注释。这样 max_tokens 约束的就不只是语法 token,而是解析器实际创建的 Token。

副作用是注释开始占配额。带大量文档注释的 SDL、由工具生成且保留注释的 schema,以及客户端里写长段说明的查询,升级后都可能被拒。公告建议这类场景调高 max_tokens,解析结果里的 token_count 口径也跟着变了。

上线前最好先从生产流量采样,算一下正常文档的注释占比,再按新口径重设上限。灰度时重点看 GraphQLSyntaxError 的比例;如果某类客户端被误伤,优先调整配额或让客户端去掉注释,不建议为了兼容直接回退版本。

还需要补的边界

解压后的请求体要有独立上限,不能只依赖 max_tokens。这个值应明显低于进程内存上限,解压过程也要放在有边界的缓冲区里。

解析阶段可以单独设超时和内存预算。它是纯 CPU 和内存操作,不依赖下游服务,适合放进隔离的执行单元;超时后直接丢弃,避免拖住整个 worker。容器内存也别卡得太紧,公告提到 256 MB 或 512 MB 的容器在几个并发请求下就可能触发 OOM。

日志里可以统计注释字符与有效字符的比例,以及请求体解压前后的体积差。正常业务查询的注释占比通常不高,几十 KB 解压成几 MB 的请求值得单独记录。解析耗时和请求体大小明显背离时,也值得回看原始文本。

学习100篇 Web3 安全文章多签架构|让智能合约安全超越私钥风险" 网络安全文章

学习100篇 Web3 安全文章多签架构|让智能合约安全超越私钥风险"

本文提出智能合约访问控制成熟度模型,分四级应对私钥失陷风险。2024年黑客盗取资金中43.8%源于密钥失陷。从单EOA控制、中心化多签、时间锁与角色分离,到彻底不可变设计,逐级提升安全性。建议评估协议层级、引入时间锁并监控、按最小权限原则划分角色,从设计早期构建运营韧性。
Backuply存在SQL注入漏洞(CVE-2026-93953)" 网络安全文章

Backuply存在SQL注入漏洞(CVE-2026-93953)"

WordPress插件Backuply存在SQL注入漏洞(CVE-2026-93953),影响1.5.8及之前所有版本。攻击者可利用BiographicalInfo字段通过恢复操作触发二阶SQL注入,以完整数据库权限执行任意SQL,将默认用户角色改为管理员并启用注册,导致权限提升和站点接管。漏洞评级高危,已在野利用,官方已发布修复补丁,建议用户立即升级至最新版本。
评论:0   参与:  0