Antipattern: שימוש בכמים חמדן במדיניות regularExpressionProtection

כרגע מוצג התיעוד של Apigee Edge.
כניסה למסמכי התיעוד של Apigee X.
מידע

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

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

הדוגמה הבאה למדיניות regularExpressionProtection מגינה על הקצה העורפי מפני התקפות של הזרקת 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>

דוגמת עיצוב

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

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

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

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

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

בהתאם לטקסט לדוגמה הבא עבור הדפוס שלמעלה:

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.* (שמשתמש בכמת לא רצוי) במקום זאת, התוצאה תהיה רק 55 שלבים.

השפעה

שימוש בכמים חמדן, כמו תווים כלליים לחיפוש (*) יחד עם StandardExpressionProtection, יכול להוביל לתוצאות הבאות:

  • עלייה בזמן האחזור הכולל של בקשות API במטען ייעודי (payload) בינוני (עד 1MB)
  • זמן רב יותר להשלמת ההפעלה של המדיניות regularExpressionProtection
  • בקשות API עם מטענים ייעודיים (payload) גדולים (יותר מ-1MB) נכשלות ונכשלות עם שגיאות זמן קצוב לתפוגה של 504 Gateway, אם חלף פרק הזמן הקצוב לתפוגה שהוגדר מראש בנתב Edge
  • ניצול גבוה של המעבד (CPU) במעבדי הודעות, בגלל עיבוד נרחב שיכול להשפיע עוד יותר על בקשות API אחרות

שיטה מומלצת

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

קריאה נוספת