تخطَّ إلى المحتوى

🔐 شرح أمن API وقواعد البيانات

أمان واجهات GraphQL

الدرس 14 من 25· ⏱ 4 دقائق قراءة

مقدمة عن أمان GraphQL

GraphQL APIs لها سطح هجوم مختلف عن REST. المستخدم يمكنه تحديد البيانات التي يطلبها، مما يفتح أبواباً لهجمات جديدة:

  • Nested Queries العميقة - استنزاف موارد الخادم
  • Batched Attacks - هجمات متزامنة
  • Introspection - كشف هيكل API بالكامل

Depth Limiting: منع الاستعلامات العميقة

الاستعلامات المتداخلة بعمق يمكن أن تسبب DoS:

# استعلام عميق يستهلك موارد كثيرة
query {
  user {
    posts {
      comments {
        user {
          posts {
            comments {
              user { name }
            }
          }
        }
      }
    }
  }
}

حدد عمق الاستعلام المسموح:

const depthLimit = require('graphql-depth-limit');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [depthLimit(5)], // أقصى عمق 5 مستويات
});

تحليل تعقيد الاستعلام (Query Complexity)

ليس كل الاستعلامات متساوية. بعضها أثقل من الآخر:

const { createComplexityLimitRule } = require('graphql-validation-complexity');

const typeDefs = `
  type Query {
    users: [User] @complexity(value: 10)
    user(id: ID!): User @complexity(value: 2)
    searchUsers(query: String!): [User] @complexity(value: 5)
  }

  type User {
    id: ID
    name: String
    posts: [Post] @complexity(value: 3)
    friends: [User] @complexity(value: 8)
  }
`;

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [
    createComplexityLimitRule(100, {
      onCost: (cost) => console.log(`Query cost: ${cost}`),
    }),
  ],
});

منع هجمات Batch

GraphQL يسمح بإرسال استعلامات متعددة في طلب واحد:

query {
  user1: user(id: 1) { name }
  user2: user(id: 2) { name }
  user3: user(id: 3) { name }
  # ... مئات المستخدمين
}

استخدم Rate Limiting مخصص لـ GraphQL:

const graphqlRateLimit = require('graphql-rate-limit');

const rateLimitDirective = graphqlRateLimit({
  identifyContext: (ctx) => ctx.user?.id || ctx.ip,
});

const typeDefs = `
  directive @rateLimit(
    max: Int,
    window: String
  ) on FIELD_DEFINITION

  type Query {
    users: [User] @rateLimit(max: 10, window: "1m")
    user(id: ID!): User @rateLimit(max: 100, window: "1m")
  }
`;

Auth Directives

استخدم directives مباشرة في Schema لتطبيق الأمان:

const { mapSchema, getDirectives, MapperKind } = require('@graphql-tools/utils');
const { defaultFieldResolver } = require('graphql');

const authDirectiveTransformer = (schema) =>
  mapSchema(schema, {
    [MapperKind.OBJECT_FIELD]: (fieldConfig) => {
      const directives = getDirectives(schema, fieldConfig);
      const authDirective = directives['auth'];

      if (authDirective) {
        const { role } = authDirective;
        const originalResolver = fieldConfig.resolve || defaultFieldResolver;

        fieldConfig.resolve = async (source, args, context, info) => {
          if (!context.user) {
            throw new AuthenticationError('يجب تسجيل الدخول أولاً');
          }

          if (role && context.user.role !== role && context.user.role !== 'admin') {
            throw new ForbiddenError('ليس لديك صلاحية');
          }

          return originalResolver(source, args, context, info);
        };
      }

      return fieldConfig;
    },
  });

const typeDefs = `
  directive @auth(role: String) on FIELD_DEFINITION

  type Query {
    users: [User] @auth(role: "admin")
    me: User @auth
  }
`;

Persisted Queries: قائمة بيضاء للاستعلامات

بدلاً من أي استعلام، اسمح فقط باستعلامات معروفة مسبقاً:

const persistedQueries = {
  'sha256hash1': `
    query GetUser($id: ID!) {
      user(id: $id) { id name email }
    }
  `,
  'sha256hash2': `
    query GetPosts {
      posts { id title }
    }
  `,
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  persistedQueries: {
    ttl: 3600, // تخزين مؤقت للاستعلامات
  },
});

تعطيل Introspection في الإنتاج

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: process.env.NODE_ENV !== 'production',
});

Throttling حسب النوع

const rateLimiters = {
  Query: {
    users: { windowMs: 60000, max: 10 },
    search: { windowMs: 60000, max: 30 },
  },
  Mutation: {
    login: { windowMs: 60000, max: 5 },
    createPost: { windowMs: 60000, max: 20 },
  },
};

أفضل الممارسات

  • طبق Depth Limiting (5-7 مستويات كحد أقصى)
  • استخدم Cost Analysis لتقييم ثقل الاستعلامات
  • طبق Rate Limiting لكل مستخدم وكل نوع استعلام
  • استخدم Auth Directives في الـ Schema نفسه
  • فعّل Persisted Queries في الإنتاج
  • عطّل Introspection في الإنتاج
  • سجل الاستعلامات غير الطبيعية
// مثال: تسجيل الاستعلامات البطيئة
server.requestDidStart((requestContext) => {
  const startTime = Date.now();

  return {
    willSendResponse() {
      const duration = Date.now() - startTime;
      if (duration > 1000) {
        console.warn('Slow query detected:', {
          query: requestContext.request.query,
          duration,
          operationName: requestContext.request.operationName,
        });
      }
    },
  };
});

ملخص

GraphQL يتطلب استراتيجيات أمان مختلفة عن REST. طبق Depth Limiting، Cost Analysis، Rate Limiting لكل استعلام، Auth Directives، و Persisted Queries لتأمين API الخاص بك.

🎯 التالي: العودة إلى قائمة الدروس

شرح أمان واجهات GraphQL — أمن API وقواعد البيانات بالعربي
أمان واجهات GraphQLأمن API وقواعد البيانات بالعربي · The Code Fix

هل كان هذا الدرس مفيدًا؟