את ModSecurity עם ערכת הכללים OWASP CRS מפעילים בעשר דקות ומכבים שלושה ימים לאחר מכן — אחרי שהמאמרים מפסיקים להישמר באזור הניהול, העלאת קבצים נשברת, ולקוח אינו מצליח לבצע הזמנה כי בכתובת שלו הופיע גרש. המסקנה «חומת האש של האפליקציה מפריעה לעבודה» מתבקשת מאליה, והיא שגויה: כמעט כל החסימות האלה נרפאות בשלוש-ארבע חריגות מדויקות, וכל התחכום הוא למצוא אותן נכון.

אל תפעילו חסימה מיד

השבוע הראשון הוא תצפית בלבד. ב-/etc/modsecurity/modsecurity.conf:

SecRuleEngine DetectionOnly

במצב הזה חומת האש רושמת כל מה שהייתה חוסמת ואינה חוסמת דבר. ושבוע של תעבורה אמיתית — כולל העבודה שלכם עצמכם באזור הניהול, העלאת תמונות וביצוע הזמנה — נותן רשימה של התראות שווא אמיתיות ולא היפותטיות. המעבר ל-On הגיוני רק אחרי שהרשימה הזאת טופלה.

איך CRS עובד: לא כלל אחד אלא סכום

זה המפתח לכל מה שבא אחר כך. CRS כמעט אף פעם אינו חוסם בקשה בכלל אחד. כל כלל שמופעל מוסיף לבקשה נקודות חריגות, והחסימה מתרחשת כשהסכום חוצה סף. לכן היומן מציג לא שורה אחת אלא כמה, ולכן האחרונה שבהן היא הכלל עם המזהה 949110 — זה שמסכם.

המסקנה המעשית: את החריגה צריך ליצור לכלל שהעניק את הנקודות, לא ל-949110. אם תכבו את הכלל המסכם, תכבו את כל הערכה, ומחומת האש תישאר שורה בקובץ הגדרות.

המסקנה השנייה היא רמת הפרנויה. כברירת מחדל היא אחת, וזו הבחירה הנכונה. רמות 2 ו-3 מוסיפות כללים שמייצרים התראות שווא באתרים רגילים מעצם התכנון, וכדאי להפעיל אותן רק אחרי שרמה אחת מכווננת במלואה.

מצאו את הכלל האשם

כל מה שדרוש נמצא ביומן הביקורת (/var/log/modsec_audit.log) וביומן השגיאות של שרת האתר. חפשו לפי שעת החסימה:

sudo grep -o 'id "[0-9]*"' /var/log/modsec_audit.log | sort | uniq -c | sort -rn | head

זו רשימת תדירות של הכללים שהופעלו. אחר כך, עבור מזהה מסוים, ראו מה בדיוק הפעיל אותו:

sudo grep -A5 'id "942100"' /var/log/modsec_audit.log | head -40

דרושים שלושה דברים: מזהה הכלל, שם הפרמטר (ARGS:content, ARGS:comment) ונתיב הבקשה. מהם נבנית החריגה.

החשודים המיידיים

הרשימה חוזרת מאתר לאתר:

  • 942100 — הזרקת SQL. מופעל בשדות טקסט עם תוכן ארוך: גוף מאמר, תיאור מוצר, תגובה. מרכאות, סוגריים ומילים כמו select נראים חשודים לגלאי בתוך פרוזה רגילה;
  • 941100 ומשפחת 941xxx — XSS. מגיעים יחד עם עורך חזותי: תגיות HTML בשדה הן כל המהות של אופן פעולתו;
  • 920420 — Content-Type לא מורשה. שובר ממשקי API והעלאת קבצים: ערכת ברירת המחדל של הסוגים המותרים צרה, ובגרסאות ישנות יותר application/json לא היה בה;
  • 913100 — סורק לפי User-Agent. תופס כלים לגיטימיים יחד עם סורקים: ניטור זמינות, ו-curl בסקריפטים שלכם עצמכם;
  • 200002, 200004 — שגיאות פענוח של גוף הבקשה. בדרך כלל אינן מציינות התקפה אלא חריגה ממגבלת גודל הגוף, כלומר העלאת קובץ גדול.

שלוש דרכים ליצור חריגה

בסדר עולה של גסות. כולן הולכות לקובץ שלכם (למשל /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES.conf) ולא לקובצי CRS עצמם: ערכת הכללים מתעדכנת והעריכות שלכם ייעלמו יחד איתה.

הסירו פרמטר אחד מתחת לכלל אחד. האפשרות המדויקת ביותר, וזו שאליה כדאי לשאוף:

SecRuleUpdateTargetById 942100 "!ARGS:content"

כבו כלל בנתיב אחד בלבד. בדיוק כשעמוד מסוים עושה רעש — עורך, ייבוא, טופס משוב:

SecRule REQUEST_URI "@beginsWith /admin/post" \
    "id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"

כבו את הכלל לחלוטין. מוצא אחרון וכמעט תמיד סימן לכך שהסיבה מעולם לא נמצאה:

SecRuleRemoveById 942100

ההבדל בין הראשונה לשלישית מהותי. במקרה הראשון שדה אחד — גוף המאמר — מפסיק להיבדק להזרקת SQL על ידי כלל אחד; בשני ובשלישי כל האתר מפסיק להיבדק. וההבדל במאמץ הוא כחמש דקות.

אחרי העריכות בדקו את ההגדרות וטענו מחדש בעדינות:

sudo apachectl configtest && sudo systemctl reload apache2
sudo nginx -t && sudo systemctl reload nginx

סדר שחוסך שבוע

  1. שבוע ב-DetectionOnly עם עבודה רגילה באתר, באזור הניהול ובהעלאות.
  2. רשימת תדירות של כללים מיומן הביקורת. עבדו מלמעלה למטה — שלושת-ארבעת הראשונים מהווים תשעים אחוז מהרעש.
  3. לכל אחד: בררו איזה פרמטר ובאיזה עמוד. צרו את החריגה לפי פרמטר ולא לפי כלל.
  4. ורק עכשיו SecRuleEngine On.
  5. פעם בחודש הציצו בחסימות: האתר השתנה, כלומר הופיעו התראות שווא חדשות.

ובדיוק בסעיף האחרון הזה הכול בדרך כלל מתפרק: יומן הביקורת הוא ג׳יגה-בייטים של טקסט ואיש לא יקרא אותו ידנית. המשמעות של לוח מקובץ היא שרשימת הכללים שהופעלו, הבקשות החסומות וערכת הכללים הפעילה יהיו לנגד עיניכם ולא יישלפו ב-grep. איך זה נראה מראה עמוד ההדגמה למטה.