保护您的GraphQL API免受漏洞侵害

哈Ha! 我向您介绍了保护GraphQL API免受安全漏洞的文章的翻译。


GraphQL迅速成为需要为其客户端应用程序创建API的开发人员的选择。 但是,像所有新技术一样,GraphQL面临一些固有的安全风险。 无论您是构建第三方项目还是大型企业应用程序,都需要确保保护自己免受这些威胁。


图片


尽管本文中列出的威胁特定于GraphQL,但是您的实现将引入一系列新的威胁,需要解决。 了解网络上运行的任何应用程序所面临的威胁也很重要。


威胁:大型,深层嵌套的查询,计算成本高昂


解决方案 :嵌套深度限制


GraphQL提供的功能与一些新的安全威胁有关。 最常见的是深度嵌套的查询,这会导致昂贵的计算和庞大的JSON,这可能会对网络及其带宽产生不利影响。


保护您的API免受此类攻击的正确方法是限制请求的深度,以便阻止恶意的深度请求,直到计算出结果为止。


GraphQL深度限制提供了一个简单的界面来限制查询深度。


import depthLimit from 'graphql-depth-limit' import express from 'express' import graphqlHTTP from 'express-graphql' import schema from './schema' const app = express() app.use('/graphql', graphqlHTTP((req, res) => ({ schema, validationRules: [ depthLimit(10) ] }))) 

威胁:暴力破解易受攻击的变异请求


解决方案 :限制请求数


搜索登录名和密码是黑客历史上最古老的技巧。 在过去的十年中,互联网上发生了如此多的数据泄漏,以至于最近发现了一个包含772,904,991个唯一电子邮件和21,222,975个唯一密码的数据库。 为了检查有关您的邮件和密码的信息是否泄漏,特洛伊·亨特(Troy Hunt)甚至创建了一个“我已被拥有”网站,除其他外,他使用了该数据库。


幸运的是,您有一种简单的方法可以使攻击者的搜索真正困难且代价高昂,这将使您对攻击者的吸引力降低。


GraphQL Rate Limit插件允许您以三种不同方式为查询指定限制:使用自定义graphql-shield指令或使用函数限制查询的基本数量。


这个插件将允许您设置一个时间窗口以及对它的请求数量的限制。 为非常脆弱的请求(例如登录)设置较大的时间窗口,为较弱的请求设置较小的时间窗口将有助于您为普通用户保持愉悦的体验,并且将成为攻击者的噩梦。


创建指令以限制请求数:


在这里,您将为每个请求需要一个唯一的标识符。 您可以使用用户的IP地址或其他标识符,这对于每个用户来说都是唯一的,并且与每个请求相对应。


 const rateLimitDirective = createRateLimitDirective({ identifyContext: (context) => { return context.id }, }) 

向您的架构添加指令:


 import { createRateLimitDirective } from 'graphql-rate-limit' export const schema = { typeDefs, resolvers, schemaDirectives: { rateLimit: rateLimitDirective, }, } export default schema 

最后,向易受攻击的查询添加指令:


 #        60  Login(input: LoginInput!): User @rateLimit( window: "60s" max: 10 message: "You are doing that too often. Please wait 60 seconds before trying again." ) 

威胁:允许用户影响用户特定的数据


解决方案 :尽可能从用户会话中获取此数据


很容易假设,如果要允许用户更新资源,那么值得让用户决定他要更新哪个资源。 但是,如果用户获得了他们实际上不应该访问的资源标识符,该怎么办?


假设我们有一个updateUser变异请求,该请求允许用户更新其个人资料。


 mutation UpdateUser($input: {"id": "test123" , "email": "test@example.com"}) { UpdateUser(input: $input) { id firstName lastName } } 

如果服务器端没有保护,则具有标识符列表的攻击者可能会为任何用户更新电子邮件地址。 显而易见的解决方案是添加检查以确保当前用户的ID与输入字段中的ID匹配。


不要这样做:


 function updateUser({ id, email }) { return User.findOneAndUpdate({ _id: id }, { email }) .catch(error => { throw error; }); } 

解决此问题的较不明显但正确的方法是防止将标识符用作输入,并使用上下文对象中的用户标识符。


这样做:


 function updateUser({ email }, context) { return User.findOneAndUpdate({ _id: context.user._id }, { email }) .catch(error => { throw error; }); } 

也许这是一个相当琐碎的示例,但是对用户直接与之交互的每个对象执行此类操作可以保护您免受许多危险错误的影响。


同时满足几个昂贵的查询


解决方案 :查询费用限制


通过为每个请求分配价格并指出每个请求的最高价格,我们可以保护自己免受可能试图同时满足太多昂贵请求的入侵者的侵害。


GraphQL Cost Analysis插件是指定查询成本和最大成本限制的简便方法。


确定最高费用:


 app.use( '/graphql', graphqlExpress(req => { return { schema, rootValue: null, validationRules: [ costAnalysis({ variables: req.body.variables, maximumCost: 1000, }), ], } }) ) 

确定每个请求的费用:


 Query: { Article: { multipliers: ['limit'], useMultipliers: true, complexity: 3, }, } 

威胁:公开GraphQL的实现细节


解决方案 :在“战斗”代码中禁用自省


GraphQL是一个非常有用的开发工具。 它是如此强大,甚至可以为您记录您的方案,请求和订阅。 对于希望在您的应用程序中发现漏洞的攻击者来说,此信息可能是金矿。


GraphQL Display Introspection插件防止您的架构泄露给公众。 只需导入插件并将其应用于您的验证规则即可。


 import express from 'express'; import bodyParser from 'body-parser'; import { graphqlExpress } from 'graphql-server-express'; + import NoIntrospection from 'graphql-disable-introspection'; const myGraphQLSchema = // ...    ! const PORT = 3000; var app = express(); app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: myGraphQLSchema, validationRules: [NoIntrospection] })); app.listen(PORT); 

Source: https://habr.com/ru/post/zh-CN481840/


All Articles