אינטליגנציה של נתונים גנרטיביים

מאחורי הקלעים: לעולם אל תסמוך על קלט משתמש

תאריך:

מאמר זה הוא הראשון בסדרת פוסטים שאני כותב על הפעלת מוצרי SaaS ואתרי אינטרנט שונים ב-8 השנים האחרונות. אני אשתף כמה מהנושאים שעסקתי בהם, לקחים שלמדתי, טעויות שעשיתי, ואולי כמה דברים שהלכו כמו שצריך. תודיע לי מה אתה חושב!

עוד ב-2019 או 2020, החלטתי לשכתב את כל ה-backend עבור חסום שולח, אפליקציית SaaS המסייעת למשתמשים ליצור חסימות דוא"ל טובות יותר, בין היתר. בתהליך, הוספתי כמה פיצ'רים חדשים ושדרגתי לטכנולוגיות הרבה יותר מודרניות. הרצתי את הבדיקות, פרסתי את הקוד, בדקתי ידנית הכל בייצור, וחוץ מכמה סיכויים אקראיים, נראה שהכל עובד מצוין. הלוואי וזה היה סוף הסיפור, אבל...

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

זה נראה כאילו ה-backend worker שמטפל בבדיקת אימיילים מול חסימות משתמש המשיך לקרוס כל 5-10 דקות. החלק המוזר ביותר - לא היו שגיאות ביומנים, הזיכרון היה בסדר, אבל ה-CPU עלה מדי פעם בזמנים אקראיים לכאורה. אז במשך 24 השעות הבאות (עם הפסקה של 3 שעות לישון - סליחה לקוחות 😬), הייתי צריך להפעיל מחדש את העובד בכל פעם שהוא קרס. מסיבה כלשהי, שירות Elastic Beanstalk חיכה זמן רב מדי להפעלה מחדש, וזו הסיבה שהייתי צריך לעשות זאת באופן ידני.

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

  • האם זה קרס על מזהה דוא"ל או סוג מסוים?
  • האם זה קרס עבור לקוח נתון?
  • האם זה התרסק במרווח קבוע כלשהו?

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

לפני שניכנס לבעיה המדויקת, בואו נדבר תחילה על אחת מהתכונות של Block Sender. בזמנו היו לי לקוחות רבים שביקשו חסימת תווים כלליים, מה שיאפשר להם לחסום סוגים מסוימים של כתובות דוא"ל לפי אותו דפוס. לדוגמה, אם תרצה לחסום את כל המיילים מכתובות דוא"ל שיווקיות, תוכל להשתמש בתו הכללי marketing@* וזה יחסום את כל האימיילים מכל כתובת שהתחילה איתה marketing@.

דבר אחד שלא חשבתי עליו הוא שלא כולם מבינים איך תווים כלליים עובדים. הנחתי שרוב האנשים ישתמשו בהם באותה דרך שאני עושה כמפתח, באמצעות אחד * לייצג כל מספר של תווים. לרוע המזל, המשתמש הספציפי הזה הניח שאתה צריך להשתמש תו כללי אחד לכל דמות שרצית להתאים. במקרה שלהם, הם רצו לחסום את כל המיילים מדומיין מסוים (שזו תכונה מקורית שיש ל-Block Sender, אבל כנראה שהם לא הבינו זאת, וזו בעיה שלמה בפני עצמה). אז במקום להשתמש *@example.com, הם השתמשו **********@example.com.

POV: צפייה במשתמשים שלך משתמשים באפליקציה שלך...
POV: צופה במשתמשים שלך משתמשים באפליקציה שלך...

כדי לטפל בתווים כלליים בשרת העובד שלנו, אנו משתמשים בספריית Node.js שידוך, שעוזר בהתאמת גלוב על ידי הפיכתו לביטוי רגולרי. הספרייה הזו תסתובב אז **********@example.com לתוך משהו כמו הביטוי הרגולרי הבא:

/[sS]*[sS]*[sS]*[sS]*[sS]*[sS]*[sS]*[sS]*[sS]*[sS]*@example.com/i

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

אז איך תיקנתי את זה? ברור שהתיקון המהיר היה למצוא את כל הבלוקים עם מספר תווים כלליים ברצף ולתקן אותם. אבל הייתי צריך גם לעשות עבודה טובה יותר בניקוי קלט המשתמש. כל משתמש יכול להזין ביטוי רגולרי ולהוריד את כל המערכת באמצעות א מתקפת ReDoS.

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

הטיפול במקרה הספציפי הזה היה די פשוט - הסר תווים כלליים עוקבים:

block = block.replace(/*+/g, '*')

אבל זה עדיין משאיר את האפליקציה פתוחה לסוגים אחרים של התקפות ReDoS. למזלנו יש מספר חבילות/ספריות שיעזרו לנו גם עם הסוגים האלה:

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

יש לך שאלות, הערות או רוצה לשתף סיפור משלך? הושט יד ב טויטר!

ספוט_ימג

המודיעין האחרון

ספוט_ימג