5月27日 22:38

GraphQL 安全有哪些最佳实践?

GraphQL 安全有哪些最佳实践?

GraphQL 的灵活查询机制在带来便利的同时,也引入了 REST 所没有的安全风险。面试中高频考察的核心问题是:如何防止恶意查询拖垮服务,以及如何控制数据访问边界。

一、防攻击层:限制查询能力

GraphQL 允许客户端自由组合查询,这使 DoS 攻击变得容易。防护手段分三层:

查询深度限制——防止无限嵌套:

javascript
const depthLimit = require('graphql-depth-limit'); const server = new ApolloServer({ typeDefs, resolvers, validationRules: [depthLimit(7)] });

查询复杂度限制——按字段权重计算总分,超标直接拒绝:

javascript
const { createComplexityLimitRule } = require('graphql-validation-complexity'); const server = new ApolloServer({ validationRules: [createComplexityLimitRule(1000)] });

速率限制——限制单位时间内的请求次数:

javascript
const rateLimit = require('express-rate-limit'); app.use('/graphql', rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));

追问:三层防护各自的适用场景?深度限制针对递归嵌套,复杂度针对广度展开,速率限制针对高频请求。三者互补,缺一不可。

二、认证授权层:控制数据访问

认证放在 context 中统一处理,授权下沉到 resolver 逐字段控制:

javascript
const server = new ApolloServer({ context: ({ req }) => { const token = req.headers.authorization || ''; try { return { user: jwt.verify(token, process.env.JWT_SECRET) }; } catch { return { user: null }; } } });

字段级权限用指令声明,resolver 中校验:

graphql
directive @auth(requires: Role) on FIELD_DEFINITION type User { email: String! @auth(requires: ADMIN) salary: Float @auth(requires: ADMIN) }

追问:为什么不能只在入口做授权?因为 GraphQL 的字段级组合查询可以绕过接口级鉴权,用户可能通过合法入口请求到未授权的敏感字段。

三、输入安全层:防注入与验证

永远不要拼接 SQL,用参数化查询或 ORM:

javascript
// 错误:字符串拼接 const query = `SELECT * FROM users WHERE id = '${userId}'`; // 正确:参数化查询 const query = 'SELECT * FROM users WHERE id = ?';

输入验证用 Yup 或 GraphQL Schema 约束指令双重保障,reject 不合规输入。

四、运维安全层:日志与错误处理

生产环境必须做到两点:错误信息脱敏(不暴露堆栈和内部结构),查询日志审计(记录 operationName 和变量,监控异常模式)。

javascript
formatError: (error) => { if (process.env.NODE_ENV === 'production') { return new Error('服务器内部错误'); } return error; }

同时禁用生产环境的 introspection 和 GraphQL 调试工具,防止 schema 泄露。

追问:introspection 禁用后如何提供文档?用代码生成工具从 schema 导出静态文档,开发环境保留 introspection,生产关闭。

五、CORS 与查询白名单

CORS 只允许可信域名访问;持久化查询(Persisted Queries)只允许预注册的查询通过,从源头阻断任意查询执行。


以上五层从前到后形成纵深防御:先限流、再鉴权、再验证输入、再脱敏输出、最后收窄查询入口。实际项目中按优先级逐步落地,不必一步到位。

标签:GraphQL