GraphQL 安全

GraphQL 提供了强大的灵活性,但也引入了独特的攻击向量:查询复杂度攻击、 内省滥用、授权绕过和信息泄露。

GraphQL 中的常见漏洞

1. 查询深度 / 复杂度攻击

深度嵌套的查询会消耗过多资源,从而导致 DoS:

      query MaliciousQuery {
      user(id: "1") {
      posts {
      comments {
      author {
      posts {
      comments {
      author {
      posts {
      # ... 无限嵌套
      }
      }
      }
      }
      }
      }
      }
      }
      }
      

2. 内省滥用

在生产环境中启用内省会暴露完整的 schema,便于攻击者进行侦察:

      query IntrospectionQuery {
      __schema {
      types {
      name
      fields {
      name
      type {
      name
      }
      }
      }
      }
      }
      

3. 授权失效

授权必须在解析器中进行,而不仅仅是在顶层查询中。当嵌套字段未检查授权时, IDOR 很常见。

4. 注入攻击

如果输入未在解析器中进行清理,GraphQL 同样无法幸免于 SQLi、NoSQLi。

必备防护措施

查询深度限制

      // Apollo Server 示例
      import depthLimit from 'graphql-depth-limit';
      const server = new ApolloServer({
      typeDefs,
      resolvers,
      validationRules: [depthLimit(5)]  // 最大深度 5
      });
      

查询复杂度分析

      import { createComplexityLimitRule } from 'graphql-validation-complexity';
      const server = new ApolloServer({
      validationRules: [
      createComplexityLimitRule(1000, {
      scalarCost: 1,
      objectCost: 5,
      listFactor: 10
      })
      ]
      });
      

速率限制

  • 基于查询:限制每个用户每分钟的查询数
  • 基于复杂度:复杂度点数预算
  • 成本分析:为每个字段分配成本

GraphQL 中的授权

字段级授权

      const resolvers = {
      Query: {
      user: async (parent, { id }, context) => {
      // 查询级授权
      if (!context.user) {
      throw new AuthenticationError('Not authenticated');
      }
      return getUserById(id);
      }
      },
      User: {
      email: (user, args, context) => {
      // 字段级授权
      if (context.user.id !== user.id && !context.user.isAdmin) {
      return null;  // 对其他用户隐藏邮箱
      }
      return user.email;
      },
      ssn: (user, args, context) => {
      // 极其敏感的字段
      if (!context.user.isAdmin) {
      throw new ForbiddenError('Admin only');
      }
      return user.ssn;
      }
      }
      };
      

基于指令的授权

      type User @auth(requires: AUTHENTICATED) {
      id: ID!
      username: String!
      email: String! @auth(requires: OWNER_OR_ADMIN)
      ssn: String! @auth(requires: ADMIN)
      }
      

内省防护

      const server = new ApolloServer({
      typeDefs,
      resolvers,
      introspection: process.env.NODE_ENV !== 'production',
      // 或按角色控制
      plugins: [{
      requestDidStart() {
      return {
      didResolveOperation({ request, context }) {
      if (request.operationName === 'IntrospectionQuery'
      && !context.user?.isAdmin) {
      throw new ForbiddenError('Introspection disabled');
      }
      }
      }
      }
      }]
      });
      

输入校验

  • Schema 校验:GraphQL 会自动校验类型
  • 自定义标量:带校验的 Email、URL、DateTime
  • 输入清理:在解析器中使用输入前先进行清理
  • 参数化查询:在数据库中使用 prepared statements

批处理与 DataLoader

防止可被利用于 DoS 的 N+1 问题:

      import DataLoader from 'dataloader';
      const userLoader = new DataLoader(async (userIds) => {
      const users = await getUsersByIds(userIds);
      return userIds.map(id => users.find(u => u.id === id));
      });
      const resolvers = {
      Post: {
      author: (post, args, { userLoader }) => {
      return userLoader.load(post.authorId);  // 已批处理!
      }
      }
      };
      

监控与日志

  • 查询日志:记录所有查询及其元数据(用户、IP、时间)
  • 错误跟踪:使用 Sentry、Datadog 跟踪异常
  • 性能监控:Apollo Studio、GraphQL Hive
  • 异常检测:对可疑查询(过于复杂等)发出告警

安全工具

  • GraphQL Armor:安全中间件套件
  • graphql-shield:带规则的权限层
  • InQL:用于 GraphQL 渗透测试的 Burp Suite 扩展
  • BatchQL:用于 GraphQL 的安全测试工具
  • graphql-cop:安全审计工具

OWASP GraphQL Top 10

  1. Broken Object Level Authorization
  2. Broken Authentication
  3. Excessive Data Exposure
  4. Resource Exhaustion
  5. Broken Function Level Authorization
  6. Mass Assignment
  7. Security Misconfiguration
  8. Injection
  9. Improper Assets Management
  10. Insufficient Logging & Monitoring

安全开发实践

  • 在所有解析器中实现授权
  • 在生产环境中禁用内省
  • 限制查询深度和复杂度
  • 积极的速率限制
  • 使用 DataLoader 防止 N+1
  • 在处理前清理输入
  • 实施全面的日志记录
  • 定期进行安全审计和渗透测试

最终建议

与 REST 相比,GraphQL 要求转变安全思维。客户端的灵活性 需要服务器端提供强健的防御。从一开始就实施深度限制、复杂度分析和 字段级授权。使用监控工具检测 滥用模式。定期使用 InQL、BatchQL 等专用工具进行测试。