לדלג לתוכן

מבוא לתקיפות HTTP/2

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

אז מה בעצם השתנה?

HTTP/1.1 עובד בצורה שרובנו מכירים אינטואיטיבית - טקסט קריא, בקשה אחת בכל פעם על כל חיבור, בתור. HTTP/2 שבר את המודל הזה מהיסוד. הוא מבוסס על מסגור בינארי (binary framing), כלומר הבקשות והתשובות מפורקות לחתיכות בינאריות קטנות, ומורכבות מחדש בצד המקבל. והכי חשוב - הוא מאפשר ריבוב (multiplexing), כלומר כמה "זרמים" (streams) של בקשות ותשובות רצים בו זמנית על אותו חיבור פיזי אחד, בלי לחכות זה לזה.

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

מה קורה כשריבוב פוגש הברחת בקשות

אחת הסיבות שהברחת בקשות (Request Smuggling) חוזרת לדיון בהקשר של HTTP/2 היא שהריבוב משנה לגמרי איך אי הסכמה בין שרתים יכולה להיראות. ב-HTTP/1.1 הבעיה נובעת בעיקר מאי בהירות סביב כותרות כמו Content-Length מול Transfer-Encoding. ב-HTTP/2 הגבולות בין בקשות אמורים להיות מוגדרים בצורה בינארית ומדויקת יותר, אבל זה בדיוק מה שהופך תרגום לא נכון בין הגרסאות למסוכן במיוחד, כפי שאני מסביר בהמשך.

דחיסת כותרות ובעיית HPACK

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

ביטול זרמים בקצב גבוה - Rapid Reset

אחת הדוגמאות המוכרות ביותר לחולשה שקיימת רק בגלל המודל של HTTP/2 היא קטגוריה שמכונה rapid reset. הרעיון הבסיסי הוא שהפרוטוקול מאפשר לצד שפותח חיבור לבקש זרם חדש, ומיד לבטל אותו. זה פיצ'ר לגיטימי לגמרי בפני עצמו. הבעיה היא שפתיחה וביטול חוזרים ונשנים בקצב גבוה מאוד יכולים לגרום לשרת להשקיע משאבים בעיבוד כל בקשה, בלי שהעלות המקבילה נופלת על הצד שפותח את הזרמים. זה בעיקרו סוג של חולשת מניעת שירות (DoS) שנובעת ישירות מהמודל של ריבוב זרמים, ולא ניתן בכלל לקרות באותה צורה בפרוטוקול הישן שבו כל בקשה תופסת חיבור משלה.

הבעיה האמיתית - התרגום בין הגרסאות

הנקודה המעניינת ביותר, ובעיניי הכי חשובה להבין, היא שרוב האינטרנט היום לא באמת מדבר HTTP/2 מקצה לקצה. הדפדפן שלכם מדבר HTTP/2 עם שכבת הפרוקסי או ה-CDN בקדמת המערכת, אבל הרבה מאוד שרתי אפליקציה מאחורי הקלעים עדיין מדברים רק HTTP/1.1. כלומר, מישהו באמצע צריך לתרגם בין הפרוטוקולים - h2 בכניסה, h1 ביציאה.

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

אז מה עושים עם זה?

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

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

הצטרפו לקהילה בדיסקורד

לסיכום

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