העברת ממשק הניהול ל-admin.example.com היא שיפור אמיתי: היא מרחיקה קובצי cookie ניהוליים מדפים ציבוריים, נותנת לשתי האפליקציות מטמונים נפרדים ומאפשרת לכוון מדיניות רשת מחמירה יותר לשם מארח אחד. אבל זה אינו בקרת CSRF, והוא אינו הופך את הקוד של האתר הציבורי לבטוח להשאיר פתוח בפני מנהל מחובר. ההפרדה משנה מה הדפדפן רואה כגבול, לא מה השרת בודק, לכן בצעו את ארבע הבדיקות האלה לפני שאתם מכריזים שזה גמור.

קראו מקור ואתר כשני מושגים שונים

מקור הוא סכימה, מארח ופורט, ולכן https://www.example.com ו-https://admin.example.com הם מקורות שונים. אתר, במובן של SameSite, הוא סכימה ועוד דומיין רשום, ולכן שני שמות המארח האלה הם בדרך כלל אותו אתר. ההבדל הזה הוא כל המלכודת: תכונת SameSite לא תעזור לכם בין תת-דומיינים אחים, כי הדפדפן בכלל לא רואה את הבקשה כחוצת-אתרים. כוונו את ממשק הניהול לדומיין רשום אחר ממש, כמו admin-example.net, ואז הבקשה אכן הופכת לחוצת-אתרים, ושם SameSite=Lax או Strict מתחיל לעשות עבודה שאפשר לסמוך עליה.

בדקו את זה בדפדפן ולא בהסקה.

טענו את האתר הציבורי, פתחו את כלי המפתחים וראו אילו קובצי cookie הדפדפן מצרף לבקשה אל מארח הניהול. אם לקובץ cookie אין תכונת Domain, הוא מוגבל למארח ונשאר על שמו שלו; אם הוא נושא Domain=example.com, הוא נוסע לכל אח, ותת-הדומיין החדש לא נתן לכם דבר עבור אותו קובץ cookie.

הפרידו קריאת תגובה משליחת בקשה

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

לכן השרת זקוק לבדיקה מפורשת לפעולות המשנות מצב. אסימון סנכרון הקשור לסשן, או קובץ cookie של שליחה כפולה שנבדק בשרת, מכסים את המקרה; השוואת Origin או Referer שווה הוספה כשער שני אך לא כשער היחיד, כי לא כל בקשה נושאת את הכותרת ולא כל פרוקסי משמר אותה. הנחיות OWASP למניעת CSRF מתייחסות ל-SameSite כשכבה אחת מבין כמה בדיוק מהסיבה הזו.

החליטו מה שער הרשת באמת מוכיח

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

שמרו עליה בלתי תלויה בבדיקות ברמת הבקשה. אם הרשימה היא הדבר היחיד בין תוקף לנקודת קצה משנת מצב, מסלול אחד שהוגדר לא נכון, לקוח split-tunnel אחד או מחשב נייד מושאל אחד מסירים את הבקרה לגמרי.

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

שמרו קובצי cookie, התחברות והרשאה במפורש

היקף קובץ ה-cookie מחליט את רוב התוצאה. קובץ cookie של דומיין האב נשלח למארח הניהול, ולכן הזרקת סקריפט באתר הציבורי יכולה לפעול עם הסשן של מנהל; קובץ cookie של המארח בלבד לא יכול. העדיפו את הצורה של המארח בלבד, הוסיפו את תכונת Secure והשתמשו בתחילית __Host- היכן שהפלטפורמה תומכת בה, כי התחילית דורשת Secure, Path=/ ובלי תכונת Domain. קראו את כותרת התגובה ולא את צנצנת קובצי ה-cookie: Set-Cookie: __Host-admin_session=...; Secure; Path=/; HttpOnly; SameSite=Lax, בלי תכונת Domain. הדפדפן משליך קובץ cookie __Host- שנושא Domain, ולכן קובץ cookie שעדיין נראה במארחים אחים הוא הוכחה שהתחילית מעולם לא הוחלה.

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

בדקו שוב את הגבול אחרי ההפרדה

הריצו את הבדיקות השליליות, לא רק את המסלול המאושר: טופס חוצה-אתרים בלי טוקן חייב להידחות, טוקן מסשן אחר חייב להידחות, ואף GET לא ישנה מצב. בדקו את תכונות קובצי ה-cookie בכלי המפתחים, ודאו שבדיקת Origin נורית בבקשה שאינה תואמת, ונסו את מארח הניהול מחוץ לטווח ה-VPN כדי לראות את המסנן בעבודה.

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

מאמרים קשורים