使用 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-yoga
或 apollo-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 的帮助,您的参考资料为我指明了正确的方向。
如何防止人们查询他们不应在 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-yoga
或 apollo-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 的帮助,您的参考资料为我指明了正确的方向。