【问题标题】:Preventing relationship data leak exploit with graphql使用 graphql 防止关系数据泄漏利用
【发布时间】:2025-12-04 19:25:01
【问题描述】:

如何防止人们查询他们不应该在 graphql 中看到的对象的关系?

例如,假设我们有以下数据模型。

type Board {
    id: ID! @unique
    name: String!
    boardUserRoles: [BoardUserRole!]!
}

type BoardUserRole {
    id: ID! @unique
    board: Board!
    user: User!
    role: BoardRole!
}

type User {
    id: ID! @unique
    tasks: [Task!]!
    userName: String! @unique
    boardUserRoles: [BoardUserRole!]!
}

这是一种设置,其中有可以访问某些板的用户。而BoardUserRole 决定了用户对特定表的角色。

现在,我过来查询一个我有权使用的板,并查看它的名称。

query {
    board(where: {id: "3"}) {
        id
        name
    }
}

这很好。但是,如果我更改查询,我可以联系系统的其他用户并查看他们的任务。我基本上可以看到与该用户相关的任何内容。

query {
    board(where: {id: "3"}) {
        id
        name
        boardUserRoles {
            user {
                tasks
            }
        }
    }
}

您如何防止此类查询?

【问题讨论】:

  • 这是个严肃的问题?这就像“我有一个带密码的 sql 表 - 如何不使用 select 显示它们” - API 根本不应该返回您没有权限的数据。不管是 sql、rest、graphql 等等。检查 prisma 文档?
  • @xadm 是的。严肃的。也许我没有正确理解某些东西,但是如果用户可以访问板 3,您将如何阻止他们使用其余模式来遍历图表以获取其他数据?你说得很明显,所以请让我知道你在想什么。我已经阅读了 prisma、Apollo 和 graphql,但没有发现任何关于如何限制某人在一个查询中要求返回整个图表的信息。我知道一种方法可以通过覆盖解析器上的info 来做到这一点,只有我希望他们获得的数据,但我不想剥夺用户的这种能力。
  • 在 graphql 中,您可以拥有字段级别的安全性。 It doesn't matter it's traversing or simple query - 解析器总是分开处理。当用户没有权限时,您应该限制返回字段。 F.e.不要显示一些数据,当你不是作者或主持人时不要更新帖子内容...... 2 秒谷歌搜索:github.com/prismagraphql/prisma/issues/1768,prisma 权限指令......
  • @xadm 这些读物非常好,我相信这正是我想要的。将在星期一对此进行更多研究。泰

标签: angular graphql apollo prisma


【解决方案1】:

在@xadm 的帮助下。我能够组合出一个非常适合我的解决方案。

读完这篇关于query execution works的文章。我想出了如何编写字段解析器。字段解析器允许我确定特定字段的解析方式。我为Board: boardUserRoles 做了一个字段解析器。

这是我的字段解析器。

Board: {
    boardUserRoles: async (parent: Board, args, ctx, info) => {
        // getting user id from header.
        const userId: string = VerificationHelper.getUserId(ctx);

        // Verifying role of user.
        return VerificationHelper.verifyRole(userId, ["ADMIN"], ctx).pipe(
            map(
                (result: boolean) => {
                    // If the user has an admin role, show the roles
                    if (result) {
                        return parent.boardUserRoles;
                    // If not, map the results to undefined
                    } else {
                        return undefined;
                    }
                }
            )
        ).toPromise();
    }
}

然后我将该解析器添加到我在 graphql 创建中的解析器列表中。

const server: GraphQLServer = new GraphQLServer({
    typeDefs: "src/schema.graphql",
    resolvers: {
        Query: QueryResolvers,
        Mutation: MutationResolvers,
        Subscription: SubscriptionResolvers,
        ...FieldResolvers, // A file that contains all my field resolvers.
        AuthPayload
    },
    context: (req: any) => ({
        ...req,
        prisma: new Prisma({
            typeDefs: "src/generated/prisma.graphql",
            endpoint: endpoint()
        })
    })
});

这是一个非常基本的字段解析器。它检查用户角色并确定是否应该隐藏数据或显示数据。我浏览了整个架构并发现了其他数据漏洞。我更正了使用字段解析器找到的那些,现在一切正常。

感谢@xadm 的帮助,您的参考资料为我指明了正确的方向。

【讨论】:

  • 太好了! ? 如果您对 Prisma 有任何其他问题,请告诉我。
  • @nburk 会做的,现在学习 graphql 并真正享受它。现在更喜欢它而不是普通的 restAPI。
【解决方案2】:

使用 Prisma 时,您可以通过重新定义应用程序架构中的类型来修改向客户端应用程序公开的数据结构。

您在问题中显示的 SDL 定义在我看来类似于 Prisma 数据模型。我假设您还有一个 API 服务器(可能使用 graphl-yogaapollo-server 实现),它公开将由您的客户端应用程序使用的 GraphQL API。我们将定义此 API 的 GraphQL 架构称为应用程序架构。在应用程序架构内,您可以重新定义数据模型中的类型,以将它们公开给客户的方式。

这是一个简单的例子:

假设您的 Prisma 数据模型中有以下 User 类型:

type User {
  id: ID! @unique
  email: String! @unique
  password: String!
  name: String!
  posts: [Post!]!
}

现在,您不想将password 公开给您的客户。因此,您可以做的是重新定义 User 类型,使您的客户应该能够访问它。以下是如何在应用程序架构中定义它(通常称为schema.graphql):

type User {
  id: ID!
  email: String!
  name: String!
  posts: [Post!]!
}

您可以找到此示例的运行版本here。此外,如果您对应用程序架构和数据模型的术语感到困惑,我建议您阅读 Prisma 文档的this 部分。

【讨论】:

  • 我完全明白你在说什么。我不知道我可以在schema.graphql 中重新定义我的类型。我认为这仅适用于解析器。这是有见地的,但不是我需要的解决方案。我需要根据授权公开某些内容。我想通了,会在几分钟内发布我的解决方案。谢谢!