使用 graphql 防止关系数据泄漏漏洞利用

Preventing relationship data leak exploit with graphql

如何防止人们查询他们不应在 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 确定用户对特定 table.

具有的角色

现在,我来查询我有权访问的板并查看其名称。

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

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

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

您如何防止此类查询?

使用 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!]!
}

您可以在 Prisma 文档的 here. Also, if you're confused by the terminology of the application schema and data model, I recommend to you reading this 部分找到此示例的 运行 版本。

在@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 的帮助,您的参考资料为我指明了正确的方向。