לפטופ אחד מול אתר של 100 מגה-ביט: איך הפכו חוקרים שירותי CDN למגבר מתקפה פי 350
מחשב נייד אחד ששולח פחות מ-500 קילו-ביט בשנייה, ובקצה השני שרת אתר שמקבל יותר מ-100 מגה-ביט בשנייה ומפסיק להגיב לגולשים אמיתיים. בין השניים אין בוטנט, אין רשת מכשירים חטופים ואין תעבורה מזויפת. יש שירות CDN מסחרי לגמרי שעושה בדיוק את מה שהוא נבנה לעשות: מקבל בקשה מהלקוח, ומעביר אותה הלאה לשרת שמאחוריו.
את התרחיש הזה תיארו שבעה חוקרים מהאוניברסיטה הלאומית של סינגפור, מאוניברסיטת פוז'ו, מאוניברסיטת שפילד ומג'ונס הופקינס במאמר CDN Tsunami, שעלה ל-arXiv ב-29 ביולי וקיבל תשומת לב רחבה בסוף השבוע האחרון אחרי סיקור ב-The Hacker News. הם בדקו שישה ספקי CDN גדולים, מצאו ששישתם חשופים לגרסה אחת של המתקפה וחמישה מהם גם לשנייה, וסרקו את הרשת כדי לספור כמה אתרים אמיתיים עומדים בתנאים. בהמשך נראה מאיפה ההגברה מגיעה, למה דווקא המעבר ל-HTTP/3 יצר אותה, ומה מתוך זה כבר תוקן.
הפער שנפתח בין שני חצאי החיבור
CDN הוא רשת של שרתי קצה מפוזרים גיאוגרפית שיושבת בין הגולש לבין האתר, מקצרת את זמן התגובה ובדרך כלל גם בולעת מתקפות מניעת שירות במקום השרת שמאחוריה. כדי לעשות את זה היא מפצלת את החיבור הישיר הישן לשניים: חיבור בין הגולש ל-CDN, וחיבור שני בין ה-CDN לשרת המקור (origin), השרת שבאמת מחזיק את האתר. שני החיבורים האלה לא חייבים לדבר באותה שפה, וזאת נקודת המוצא של כל המחקר.
HTTP/3 תוקנן ב-2022 ורץ מעל QUIC במקום מעל TCP. הוא נועד לפתור בדיוק את מה שהכביד על הגרסאות הקודמות: פחות עיכובים בפתיחת החיבור, דחיסת כותרות טובה יותר, וריבוב שמאפשר להריץ הרבה בקשות במקביל על חיבור יחיד בלי שאיבוד חבילה אחת יתקע את כל השאר. החוקרים בדקו את שישה הספקים ומצאו תמונה אחידה לגמרי: Alibaba, Baidu, Cloudflare, CloudFront, Fastly ו-Tencent תומכים כולם ב-HTTP/3 מול הגולש, וכולם מדברים HTTP/1.1 מול שרת המקור. גם כשהאתר עצמו יודע HTTP/3.
התרגום הזה, מ-HTTP/3 בצד אחד ל-HTTP/1.1 בצד השני, הוא כל הסיפור.
איך אינדקס קטן הופך לכותרת שמנה
רוב הכותרות בבקשת HTTP חוזרות על עצמן שוב ושוב: אותו User-Agent, אותו Cookie, אותם שדות סטנדרטיים. במקום לשלוח את המחרוזת המלאה בכל בקשה, HTTP/3 מאפשר לשלוח מספר סידורי קצר שמצביע על טבלה ששני הצדדים מחזיקים אצלם. לשיטת הדחיסה הזאת קוראים QPACK, ויש בה שתי טבלאות: טבלה סטטית עם שדות נפוצים שמוגדרת מראש בפרוטוקול, וטבלה דינמית שנבנית תוך כדי החיבור מהכותרות שכבר עברו בו.
ה-CDN, שמדבר HTTP/1.1 מול שרת המקור, חייב לפרוס את כל האינדקסים האלה בחזרה לכותרות מלאות לפני שהוא מעביר את הבקשה הלאה, כי HTTP/1.1 לא מכיר דחיסת כותרות בכלל. הבקשה שנכנסה קטנה יוצאת החוצה גדולה בהרבה, ומי שמשלם על ההפרש בתעבורה הוא שרת המקור. לגרסה הזאת של המתקפה קראו החוקרים HTTP/3 Bandwidth Amplification, בקיצור HBA.
עם הטבלה הסטטית לבדה, מקדמי ההגברה שנמדדו נעים בין 36.41 ב-Fastly ל-66.06 ב-Baidu, כשבאמצע Alibaba עם 65.8, Tencent עם 54.08, CloudFront עם 51.2 ו-Cloudflare עם 48.27. הטבלה הדינמית מקפיצה את זה בסדר גודל: התוקף מכניס אליה פעם אחת ערך גדול (שלושת הספקים שתומכים בה מגבילים כניסה בודדת ל-3,072 בתים), ומכאן והלאה מצביע עליו שוב ושוב באינדקס זעיר. ההגברה המקסימלית שנמדדה בדרך הזאת עומדת על כפי 350.
המספר המעניין הוא דווקא איפה השיא הזה יושב. הוא לא מתקבל בתקרת הזרמים המקבילים שה-CDN מרשה, 128, אלא סביב 64. מעבר לנקודה הזאת המעבד של שרת הקצה עצמו הופך לצוואר הבקבוק, כי הוא חייב לפרוס כל כותרת דחוסה לפני ההעברה, והזרמים הנוספים ממתינים בתור. גבול ההגברה נקבע ביכולת העיבוד של ה-CDN, לא בכמות שהתוקף מוכן לשלוח.
ובשביל פרופורציה: מתקפת CDN Judo, שניצלה בזמנה בדיוק את אותו רעיון במעבר מ-HTTP/2 ל-HTTP/1.1, הגיעה לכ-44 עם הטבלה הסטטית ולכ-166 עם הדינמית. המעבר ל-HTTP/3 יותר מהכפיל את המספר.
עד כאן הצד של רוחב הפס. הגרסה השנייה לא נוגעת ברוחב פס בכלל, והיא במובן מסוים אלגנטית יותר.
למה מספיק להתחיל בקשה ולא לסיים אותה
כדי לזרז את הכל, רוב ספקי ה-CDN פותחים את החיבור לשרת המקור ברגע שהגיעו אליהם כותרות הבקשה, בלי להמתין לגוף הבקשה. התוקף מנצל בדיוק את זה: הוא שולח את הכותרות, ה-CDN ממהר לפתוח מולן חיבור TCP אל שרת המקור, ואז התוקף מטפטף את שאר הבקשה בקצב של פריים אחד בשנייה. מבחינת ה-CDN הבקשה עדיין לא הסתיימה, ולכן החיבור לשרת המקור נשאר פתוח. לגרסה הזאת קראו החוקרים HTTP/3 Connection Amplification, או HCA.
הריבוב של HTTP/3 הוא מה שהופך את זה מטריק לנשק. כל זרם בתוך חיבור HTTP/3 יחיד גורם ל-CDN לפתוח חיבור TCP נפרד ומלא מול שרת המקור, כי HTTP/1.1 לא יודע לרבב בקשות. בניסוי הספיקו ארבעה חיבורי HTTP/3 עם 96 זרמים בכל אחד כדי לגרום ל-CDN לפתוח 384 חיבורים מול שרת הניסוי, שהוגדר עם תקרה של 256 חיבורים. Fastly, שמגביל את עצמו לעשרה חיבורי מקור לכל חיבור HTTP/3, דרש דרך עוקפת: 48 חיבורים עם שמונה זרמים בכל אחד, ואותה תוצאה בדיוק.
מה קרה למשתמשים תמימים באותו זמן? החוקרים שלחו בקשת GET רגילה כל שנייה ומדדו את זמן התגובה. מול Alibaba הוא טיפס ל-60 שניות, מול Baidu ומול CloudFront לכ-90 שניות ואז HTTP 504, מול Fastly ל-15 שניות ואז HTTP 503, ומול Tencent החיבור פשוט נסגר אחרי כעשר שניות בלי שום תשובה. המתקפה החזיקה 300 שניות.
ספק אחד יצא מזה נקי. Cloudflare לא פותח את החיבור לשרת המקור עד שהבקשה הגיעה במלואה, ובמדידה זה נראה בדיוק ככה: אצלו זמן פתיחת החיבור אחורה היה 301 שניות, לעומת שתיים או שלוש שניות אצל האחרים. הגישה הזאת עולה קצת בזמן תגובה, ובתמורה היא מנטרלת את הווריאנט הזה לחלוטין.
כמה אתרים באמת עומדים בתנאים
את תוצאות המעבדה צריך לתרגם למספר אמיתי, ולשם כך בנו החוקרים מסגרת מדידה אוטומטית. הם ייצרו רשימת תת-דומיינים לדומיינים שברשימת Tranco Top 1M, שלפו לכל אחד רשומות CNAME ו-NS, השוו אותן לתת-הדומיינים שספקי ה-CDN מקצים ללקוחותיהם, ולבסוף פנו לכל מועמד בלקוח HTTP/3 כדי לראות מי מהם באמת עונה ב-HTTP/3.
התוצאה: 151,685 תת-דומיינים מתארחים על CDN חשוף, ומתוכם 42,330 עומדים בכל התנאים למתקפה. הפילוח לפי ספק מגלה איפה מרוכז המשקל. ב-CloudFront נמצאו 77,724 דומיינים שמתוכם 17,431 חשופים, ב-Cloudflare 32,590 שמתוכם 12,371, ב-Fastly 22,475 שמתוכם 11,606, ב-Alibaba 12,174 שמתוכם 720, ב-Tencent 3,465 שמתוכם 184, וב-Baidu 3,257 שמתוכם 18 בלבד.
שווה לקרוא את המספר הזה בדיוק כפי שהוא כתוב. 42,330 הם אתרים שעומדים בתנאים המוקדמים, לא אתרים שנתקפו. החוקרים תקפו רק שרת שהקימו לעצמם, והגבילו אותו מראש ל-100 מגה-ביט ואת עצמם ל-30, כדי לא לפגוע בלקוחות אחרים של אותו CDN. הם מדגישים שהמגבלות האלה הן אתיות ולא טכניות, ושיחס ההגברה נובע ממנגנון התרגום עצמו ולא מגודלו של הקורבן.
מה ענו הספקים
החוקרים דיווחו לכל שישה הספקים. כולם אישרו קבלה, שניים הגיבו בפועל. Baidu דירג את HCA כחומרה בינונית ואת HBA כחומרה גבוהה, שילם באג באונטי של כ-350 דולר, והטמיע את ההמלצות. Tencent דירג את HCA כבינוני, שילם כ-150 דולר, והטמיע הגבלה על מספר החיבורים מול שרת המקור ועל גודל הכותרות שאפשר להכניס לטבלה הדינמית. שאר הספקים, לפי המאמר, עדיין דנים בממצאים פנימית. (שני דיווחים שמאפשרים להשבית אתר מלפטופ אחד, בסך הכל כחצי אלף דולר.)
ההמלצות עצמן פשוטות למדי, וכולן יושבות בצד ה-CDN: לתחום את גודל הערך שנכנס לטבלה הדינמית, להגביל כמה פעמים אותו אינדקס יכול לחזור בזרם בודד, לקבוע תקרה לגודל הבקשה אחרי הפריסה, ולא לפתוח חיבור לשרת המקור לפני שהבקשה הושלמה. השורש, כותבים החוקרים, הוא שספקי ה-CDN לא תומכים ב-HTTP/3 מקצה לקצה, ושתקן HTTP/3 עצמו לא נותן שום הנחיה איך לאבטח את התרגום בין הגרסאות.
ולמי שתוהה כמה מאמץ נדרש מהתוקף: שלושה מתוך שישה הספקים, Baidu, Cloudflare ו-Fastly, מסתפקים בכתובת מייל תקינה כדי לפתוח חשבון חינמי, וכולם למעט רשימה שחורה של דומיינים ב-Cloudflare לא מוודאים שמי שמחבר דומיין לשירות הוא באמת בעליו.
מה זה אומר בפועל
לבעלי אתרים אין כאן טלאי להתקין. כל התיקונים שהחוקרים מציעים יושבים אצל ספק ה-CDN, ומי שמפעיל אתר יכול בעיקר לבדוק אם הוא בכלל בתמונה: האם HTTP/3 מופעל בהפצה שלכם (ב-Cloudflare וב-CloudFront הוא דלוק כברירת מחדל, אצל ארבעת האחרים כבוי וניתן להפעלה), מה תקרת החיבורים המקבילים ומה ה-timeout שמוגדרים בשרת המקור עצמו, ואם הספק שלכם ממתין לבקשה שלמה לפני שהוא פותח חיבור אחורה.
הנקודה הפחות נוחה היא הזיהוי. התעבורה שמגיעה לשרת המקור מגיעה מכתובות ה-IP של ה-CDN, בבקשות HTTP/1.1 תקינות לחלוטין, ונראית בדיוק כמו יום עמוס במיוחד. מנגנוני ההגנה שנועדו לסנן שיטפון תעבורה זדונית לא באמת יודעים להבדיל בין השניים.
תגובות