אבטחת GraphQL

GraphQL מציע גמישות עוצמתית אך מכניס וקטורי תקיפה ייחודיים: התקפות query complexity, ניצול לרעה של introspection, עקיפת הרשאה וחשיפת מידע.

פגיעויות נפוצות ב-GraphQL

1. התקפות Query Depth / Complexity

שאילתות מקוננות לעומק עלולות לגרום ל-DoS על ידי צריכת משאבים מופרזת:

      query MaliciousQuery {
      user(id: "1") {
      posts {
      comments {
      author {
      posts {
      comments {
      author {
      posts {
      # ... מקונן עד אינסוף
      }
      }
      }
      }
      }
      }
      }
      }
      }
      

2. ניצול לרעה של Introspection

introspection מופעל בסביבת ייצור חושף את הסכמה המלאה ומקל על reconnaissance:

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

3. הרשאה שבורה

הרשאה חייבת להתבצע ב-resolvers, ולא רק בשאילתות ברמה העליונה. IDOR נפוץ כאשר ההרשאה אינה נבדקת בשדות מקוננים.

4. התקפות Injection

GraphQL אינו חסין מפני SQLi או NoSQLi אם הקלטים אינם עוברים סניטציה ב-resolvers.

הגנות חיוניות

הגבלת Query Depth

      // דוגמה ל-Apollo Server
      import depthLimit from 'graphql-depth-limit';
      const server = new ApolloServer({
      typeDefs,
      resolvers,
      validationRules: [depthLimit(5)]  // עומק מרבי 5
      });
      

ניתוח Query Complexity

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

Rate Limiting

  • מבוסס שאילתות: הגבלת שאילתות לדקה לכל משתמש
  • מבוסס complexity: תקציב של complexity points
  • ניתוח עלות: הקצאת עלות לכל שדה

הרשאה ב-GraphQL

הרשאה ברמת השדה

      const resolvers = {
      Query: {
      user: async (parent, { id }, context) => {
      // הרשאה ברמת ה-query
      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;
      }
      }
      };
      

הרשאה מבוססת directives

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

הגנה על Introspection

      const server = new ApolloServer({
      typeDefs,
      resolvers,
      introspection: process.env.NODE_ENV !== 'production',
      // או שליטה לפי role
      plugins: [{
      requestDidStart() {
      return {
      didResolveOperation({ request, context }) {
      if (request.operationName === 'IntrospectionQuery'
      && !context.user?.isAdmin) {
      throw new ForbiddenError('Introspection disabled');
      }
      }
      }
      }
      }]
      });
      

אימות קלט

  • אימות סכמה: GraphQL מאמת טיפוסים באופן אוטומטי
  • Custom scalars: Email, URL, DateTime עם אימות
  • סניטציית קלט: ביצוע סניטציה לקלטים לפני שימוש בהם ב-resolvers
  • שאילתות פרמטריות: שימוש ב-prepared statements במסד הנתונים

Batching ו-DataLoader

מניעת בעיית ה-N+1, שעלולה להיות מנוצלת ל-DoS:

      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);  // באצ'!
      }
      }
      };
      

ניטור ורישום (Logging)

  • רישום שאילתות: תיעוד כל השאילתות עם metadata (משתמש, IP, זמן)
  • מעקב שגיאות: Sentry, Datadog עבור exceptions
  • ניטור ביצועים: Apollo Studio, GraphQL Hive
  • זיהוי אנומליות: התרעה על שאילתות חשודות (מורכבות מדי וכו')

כלי אבטחה

  • GraphQL Armor: חבילת middleware לאבטחה
  • graphql-shield: שכבת הרשאות עם rules
  • InQL: תוסף Burp Suite ל-pentest של GraphQL
  • BatchQL: כלי security testing ל-GraphQL
  • graphql-cop: כלי security auditing

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

פרקטיקות פיתוח מאובטח

  • יישום הרשאה בכל ה-resolvers
  • השבתת introspection בסביבת ייצור
  • הגבלת query depth ו-complexity
  • rate limiting אגרסיבי
  • שימוש ב-DataLoader למניעת N+1
  • סניטציה לקלטים לפני עיבוד
  • יישום logging מקיף
  • ביקורות אבטחה ו-pentests סדירים

המלצות לסיכום

GraphQL דורש שינוי בתפיסת האבטחה בהשוואה ל-REST. הגמישות של ה-client מחייבת הגנות חזקות בצד ה-server. ישמו depth limiting, ניתוח complexity והרשאה ברמת השדה כבר מההתחלה. השתמשו בכלי ניטור לזיהוי דפוסי ניצול לרעה. בדקו באופן קבוע עם כלים ייעודיים כמו InQL ו-BatchQL.