אנטי-תבנית: שימוש בכמתים חמדנים במדיניות RegularExpressionProtection

הדף הזה רלוונטי ל-Apigee ול-Apigee Hybrid.

לעיון במסמכי התיעוד של Apigee Edge

מדיניות RegularExpressionProtection מגדירה ביטויים רגולריים שמוערכים בזמן הריצה על פרמטרי קלט או משתני זרימה. בדרך כלל משתמשים במדיניות הזו כדי להגן מפני איומי תוכן כמו הזרקת SQL או JavaScript, או כדי לבדוק פרמטרים של בקשות שגויות כמו כתובות אימייל או כתובות URL.

אפשר להגדיר את הביטויים הרגולריים עבור נתיבי בקשות, פרמטרים של שאילתות, פרמטרים של טפסים, כותרות, רכיבי XML (במטען ייעודי (payload) של XML שמוגדר באמצעות XPath), מאפיינים של אובייקט JSON (במטען ייעודי (payload) של JSON שמוגדר באמצעות JSONPath).

המדיניות הבאה מסוג RegularExpressionProtection מגנה על ה-backend מפני מתקפות הזרקת SQL:

<!-- /antipatterns/examples/greedy-1.xml -->
<RegularExpressionProtection async="false" continueOnError="false" enabled="true"
  name="RegexProtection">
    <DisplayName>RegexProtection</DisplayName>
    <Properties/>
    <Source>request</Source>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <QueryParam name="query">
      <Pattern>[\s]*(?i)((delete)|(exec)|(drop\s*table)|
        (insert)|(shutdown)|(update)|(\bor\b))</Pattern>
    </QueryParam>
</RegularExpressionProtection>

תבנית אנטי

הכמתים שמוגדרים כברירת מחדל (*,‏ + ו-?) הם חמדנים: הם מתחילים להתאים לרצף הארוך ביותר האפשרי. אם לא נמצאת התאמה, המערכת חוזרת אחורה בהדרגה כדי לנסות להתאים את התבנית. אם המחרוזת שמתקבלת בהתאמה לתבנית קצרה מאוד, שימוש בכמתים חמדנים עלול לקחת יותר זמן מהנדרש. זה נכון במיוחד אם מטען הייעוד גדול (עשרות או מאות קילובייט).

בדוגמה הבאה לביטוי נעשה שימוש בכמה מופעים של .*, שהם אופרטורים חמדנים:

<Pattern>.*Exception in thread.*</Pattern>

בדוגמה הזו, מדיניות RegularExpressionProtection מנסה קודם להתאים את הרצף הארוך ביותר האפשרי – המחרוזת כולה. אם לא נמצאה התאמה, המדיניות חוזרת אחורה בהדרגה. אם המחרוזת התואמת קרובה להתחלה או לאמצע של מטען הייעודי (payload), שימוש בכמת חמדן כמו .* יכול לקחת הרבה יותר זמן וכוח עיבוד מאשר כמתים לא חמדנים כמו .*? או (במקרים פחות נפוצים) כמתים בעליים כמו .*+.

כמתחים לא חמדנים (כמו X*?, X+?, X??) מנסים להתאים תו בודד מתחילת המטען הייעודי (payload) ומוסיפים בהדרגה תווים. כמתחים בעליות (כמו X?+, X*+, X++) מנסים להתאים את כל המטען הייעודי רק פעם אחת.

בהינתן הטקסט לדוגמה הבא לתבנית שלמעלה:

Hello this is a sample text with Exception in thread
with lot of text after the Exception text.

השימוש ב-.* החמדן לא יעיל במקרה הזה. הדפוס .*Exception in thread.* דורש 141 שלבים כדי להתאים. אם במקום זאת השתמשתם בדפוס .*?Exception in thread.* (שמשתמש בכמת מסוג reluctant), התוצאה הייתה רק 55 שלבים.

השפעה

שימוש בכמתים חמדנים כמו תווים כלליים לחיפוש (*) עם מדיניות RegularExpressionProtection עלול להוביל ל:

  • עלייה בזמן האחזור הכולל של בקשות API לגבי מטען ייעודי (payload) בגודל בינוני (עד 1MB)
  • זמן ארוך יותר להשלמת ההפעלה של מדיניות RegularExpressionProtection
  • בקשות API עם מטען ייעודי גדול (>‎1MB) נכשלות עם שגיאות 504 Gateway Timeout אם חלף פרק הזמן המוגדר מראש של פסק הזמן ב-Apigee Router
  • ניצול גבוה של CPU במעבדי הודעות בגלל כמות גדולה של עיבוד, שיכולה להשפיע עוד יותר על בקשות API אחרות

שיטה מומלצת

  • מומלץ להימנע משימוש בכמתים חמדנים כמו .* בביטויים רגולריים עם מדיניות RegularExpressionProtection. במקום זאת, כדאי להשתמש בכמתים לא חמדנים כמו .*? או בכמתים של בעלות כמו .*+ (פחות נפוץ) בכל מקום שאפשר.

קריאה נוספת