זיהום פרמטרים - HTTP Parameter Pollution - כשלשלוח פרמטר פעמיים משנה הכל¶
בקשת HTTP שכוללת את אותו שם פרמטר יותר מפעם אחת, כמו ?id=1&id=2, נראית כמו משהו שאמור להיות שולי לחלוטין. הבעיה היא שאין תשובה אחת ל"מה קורה כשזה מגיע לשרת" - כל שכבה בדרך, החל מה-WAF ועד לשפת התכנות של האפליקציה, יכולה להחליט אחרת לגמרי מה לעשות עם הערך הכפול הזה. הפער הזה נקרא HTTP Parameter Pollution, והוא אחת הטכניקות הפחות מוכרות שיכולות לפתוח חורים ממשיים באפליקציות שנראות תקינות לחלוטין.
למה בכלל יש חוסר עקביות¶
כשמקבלים בקשה עם פרמטר כפול, כל טכנולוגיה בצד השרת צריכה להחליט איך לטפל בזה, ואין תקן אחיד מחייב בעולם ה-HTTP שקובע את התשובה. בפועל, שפות ומסגרות עבודה שונות בחרו התנהגויות שונות לגמרי:
- לוקחים רק את הערך הראשון - חלק מהמימושים פשוט מתעלמים מכל מופע נוסף ומשתמשים בערך הראשון שהופיע בבקשה.
- לוקחים רק את הערך האחרון - מימושים אחרים דורסים כל ערך קודם, כך שרק המופע האחרון נשאר בפועל.
- מרכזים לכל הערכים למחרוזת אחת - יש שרתים שמחברים את כל הערכים המופיעים לרשימה אחת מופרדת בפסיקים או ברווח.
- יוצרים מערך של כל הערכים - טכנולוגיות אחרות בונות מבנה נתונים שמכיל את כל הערכים כרשימה, ומעבירות אותה הלאה כמות שהיא לקוד האפליקציה.
המשמעות המעשית היא שבקשה אחת בדיוק יכולה להתפרש בצורה שונה לגמרי בהתאם לטכנולוגיה שמקבלת אותה, ואם יש יותר משכבה אחת שמעורבת בטיפול בבקשה - למשל WAF ואז שרת אפליקציה, או שרת אפליקציה ואז מערכת פנימית שהפרמטר מועבר אליה הלאה - כל שכבה יכולה לקרוא ערך שונה מהאחרת.
עקיפת ולידציה כשהבודק בודק ערך אחד והשרת מריץ אחר¶
התרחיש הקלאסי הוא כזה - שכבת ולידציה, בין אם ב-WAF ובין אם בקוד עצמו, בודקת רק את הערך הראשון של פרמטר, ומוצאת שהוא תקין ולא חשוד. אבל השרת עצמו, במקום שממש את הלוגיקה העסקית, לוקח את הערך האחרון. תוקף יכול לשלוח את אותו פרמטר פעמיים - פעם עם ערך תמים שעובר את הבדיקה, ופעם שנייה עם ערך זדוני שהשרת בסוף באמת מריץ:
אם שכבת האימות בודקת רק את המופע הראשון של amount ומוצאת שהוא בטווח סביר, אבל הלוגיקה בפועל שמבצעת את ההעברה משתמשת בערך האחרון, כל בדיקת הגבלת סכום נעקפת לחלוטין בלי לגעת בכלום מלבד סדר הפרמטרים בבקשה.
בלבול לוגיקת הרשאות¶
חוסר עקביות דומה יכול לפגוע גם בשכבת ההרשאות. תארו לעצמכם אפליקציה שמעבירה פרמטר role בבקשת יצירת משתמש. אם קוד הבדיקה בצד השרת בודק את כל הערכים ומוודא שאף אחד מהם לא admin, אבל קוד היצירה בפועל משתמש רק בערך האחרון, שליחת role=user&role=admin יכולה לגרום לבדיקה לעבור בעוד המשתמש בפועל נוצר עם הרשאות מנהל.
זיהום שמועבר הלאה למערכת פנימית¶
תרחיש שלישי, ומעניין במיוחד, קורה כשפרמטר חשוד לא מטופל ישירות אלא מועבר הלאה לבקשה פנימית - לדוגמה, שרת חזית שמקבל פרמטר חיפוש ומעביר אותו כמות שהוא לשירות פנימי, כמו מסד נתונים או API נוסף. אם שרת החזית מנקה ומוודא רק את הערך הראשון, אבל בונה את הבקשה הפנימית בצורה שמעבירה את כל המופעים הכפולים הלאה, השירות הפנימי עלול לקבל ערך שמעולם לא עבר את הבדיקה המקורית בכלל, כי הוא רק "נסע" יחד עם הערך התקין בלי להיבדק בנפרד.
איך מגנים נכון¶
- טיפול מפורש בפרמטרים כפולים - קוד השרת צריך להחליט במפורש איך להתייחס לפרמטר שמופיע יותר מפעם אחת, ולא לסמוך על ברירת המחדל של הפריימוורק בלי לדעת מה היא.
- פרסור קפדני ועקבי - כל שכבה שמטפלת בבקשה, מ-WAF ועד הקוד העסקי, צריכה להתייחס לפרמטרים כפולים באותה צורה בדיוק, כדי לסגור את הפער שמאפשר את הטכניקה מלכתחילה.
- דחיית בקשות חשודות - הגישה הבטוחה ביותר במקרים רגישים היא לדחות לגמרי בקשה שמכילה פרמטר כפול במקום שאמור להיות ערך יחיד, במקום לנחש איזה ערך "נכון".
- בדיקת התנהגות בפועל, לא הנחה - הדרך היחידה לדעת בוודאות איך מערכת מסוימת מתנהגת מול פרמטרים כפולים היא לבדוק בפועל בכל שכבה, ולא להניח שההתנהגות זהה בכל מקום שבו הבקשה עוברת.
זיהום פרמטרים הוא דוגמה מצוינת לכך שהחולשות המעניינות ביותר לא תמיד נמצאות בקוד עצמו, אלא בפער בין איך ששכבות שונות של המערכת מפרשות את אותה בקשה בדיוק. זה סוג החשיבה השיטתית שאנחנו מפתחים בקורס פריצת אתרים מתקדמת.
נתקלתם בהתנהגות משונה כשניסיתם פרמטר כפול על אפליקציה? זה בדיוק המקום לשתף בדיסקורד ולראות איך אחרים ניגשים לזה.
לסיכום¶
HTTP Parameter Pollution מנצל את חוסר האחידות בעולם ה-HTTP סביב פרמטרים כפולים - כל שכבה יכולה לפרש אותם אחרת, ופער כזה בין שכבת ולידציה לשכבת ביצוע בפועל יכול לעקוף בדיקות, לבלבל הרשאות, ולזהם בקשות פנימיות. ההגנה מתחילה בהחלטה מפורשת ועקבית איך לטפל בפרמטר כפול, ולא בהנחה שקטה שהוא פשוט "לא יקרה".