Skip to content

[安全] 全系统缺少认证与授权,存在越权访问(IDOR) #16

Open

Description

@yuezengwu

cc @wyzBelinda

问题

系统所有 HTTP/WebSocket 接口均无认证与授权:

  • api/middleware.py 中仅定义了 AuthenticationError/AuthorizationError 异常类,从未被任何路由使用。
  • 资源接口以路径参数 patient_id 直接取数据,无归属校验,构成越权访问(IDOR):
    • modules/RareSystem/backend/api/cases.py:119 —— GET /api/cases/{patient_id}
    • modules/RareSystem/backend/api/cases.py:253 —— DELETE /api/cases/{patient_id}(会连带删除磁盘上的 VCF 文件)
  • CORS 配置过宽:main.py:58-64 使用 allow_credentials=True 且 allow_methods=["*"]、allow_headers=["*"]。

影响

任何可访问服务的人可枚举 patient_id 读取或删除任意患者的基因组数据与病例。基因组数据属高敏感健康数据,越权访问是合规红线。

解决方案

  1. 在网关引入统一认证(JWT/OAuth 或 API Key + mTLS),下游服务只接受经网关转发的已认证请求。
  2. 为所有资源接口增加基于用户身份的授权校验,消除 IDOR(校验请求者是否有权访问该 patient_id)。
  3. 收紧 CORS:allow_origins 用明确白名单,allow_methods/allow_headers 按需列举。

优先级

高。对外/生产部署前必须解决。

Activity

  1. wen1112 commented on Jul 6, 2026

    @wen1112
    Collaborator
  2. FenjuFu commented on Jul 21, 2026

    @FenjuFu
    Contributor

    这三个安全 issue 里,我认为只有这个是真正的架构决策,所以不打算直接甩 PR,先把方案和取舍列出来等你们定。#15 / #17 已提 PR(#29 / #30)。

    先更正一处

    CORS 配置过宽:main.py:58-64 使用 allow_credentials=True 且 allow_methods=["*"]、allow_headers=["*"]

    allow_origins 用的是 CORS_ORIGINS 环境变量,默认值是 http://localhost:8888,http://127.0.0.1:8888,http://localhost:8181,http://127.0.0.1:8181 —— 是明确白名单,不是 *。在 origin 已经受限的前提下,allow_credentials=True 是正常配置,allow_methods/headers=["*"] 的实际风险很低。

    所以我建议把 CORS 从这个 issue 里摘出去(或降为 low):它现在不是漏洞,收紧它也不会减少任何实际暴露面。真正的问题是下面两条 —— 没有身份,和没有归属校验。把 CORS 和它们并列,容易让人以为改完 CORS 就安全了。

    真正要定的三件事

    1. 这个系统的多用户模型是什么?

    这是前置问题,不定它后面全是空谈。三种可能,请选:

    • (a) 单用户 / 单机自托管:一个研究者在自己机器上跑。那就不存在 IDOR —— 所有数据本来就都是他的。需要的只是「别让别人连上来」,也就是 [安全] 服务默认绑定 0.0.0.0,建议改为回环地址 127.0.0.1 #15 + 一个可选的静态 API Key。
    • (b) 单机构多用户:一个医院/实验室内多个医生共用一个部署,各自的病例互相不可见。需要用户表 + patient.owner_id + 归属校验。
    • (c) 多租户 SaaS:跨机构。还要加租户隔离、审计日志、数据驻留等,工作量是 (b) 的数倍。

    从 README 的部署方式(pixi 本地装、1TB 本地数据、OpenRare Lite 那个 #8)看,现状明显是 (a),而 issue 描述里的 IDOR 是按 (b)/(c) 写的。如果短期目标就是 (a),那这个 issue 的正确解法不是「加认证授权」,而是「文档明确写清:本系统假定单一可信用户,禁止暴露到不可信网络」+ #15 那种默认回环 —— 成本几乎为零,且诚实。

    我倾向:先明确声明为 (a),把 (b) 作为单独的 roadmap issue,而不是现在就往里塞一套半成品的认证。半成品认证比没有认证更危险,因为它会让人以为可以对外暴露了。

    2. 如果要做 (b),认证放哪一层?

    issue 的方案 1 说「网关引入统一认证,下游只接受经网关转发的已认证请求」。这里有个坑:下游模块目前是 7 个独立 uvicorn 进程,gateway 只是透明转发(/m/{module}/{path} 直接 httpx 打 127.0.0.1:<port>)。「下游只接受网关转发」这件事,在同机回环上没有任何强制手段 —— 任何本机进程都能直接连 8001-8007 绕过网关。

    所以要么:

    • 网关认证 + 下游继续裸奔,安全边界靠 [安全] 服务默认绑定 0.0.0.0,建议改为回环地址 127.0.0.1 #15 的回环绑定(够用,前提是接受「本机即可信」);
    • 或者下游各自校验一个网关注入的共享 secret(简单,能挡住「本机其他用户」这一档);
    • 或者真做 mTLS(重,我不推荐,这个规模不值得)。

    3. patient_id 是自增整数

    GET/DELETE /api/cases/{patient_id} 用的是自增 ID,所以即使加了归属校验,枚举本身仍然会泄露「系统里有多少病例」。如果要做 (b),建议顺带把对外暴露的标识换成 UUID。这个改动越早越便宜。


    我能立刻做、且无论选 (a) 还是 (b) 都不白费的,有两件:

    1. DELETE /api/cases/{patient_id} 目前会连带删除磁盘上的 VCF 文件且无任何确认。加一个「软删除 + 保留文件」或至少要求显式 ?purge_files=true,这是纯粹的防误操作改进,跟认证方案无关。
    2. 给 README / 部署文档加一节明确的安全边界声明(当前假定单一可信用户、禁止暴露公网、如何正确放到反代后面)。

    要我先做这两件吗?认证本身我等你们对第 1 点的答复。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions