עקיפת CSP מוסברת - מתי מדיניות אבטחה טובה על הנייר לא מגנה בפועל¶
CSP, מדיניות אבטחת התוכן, נבנתה כדי לצמצם את הנזק שחולשת XSS יכולה לגרום - גם אם תוקף מצליח להזריק קוד, ה-CSP אמור למנוע ממנו להריץ אותו. אבל CSP הוא רק כלל שהדפדפן אוכף, וכללים אפשר לנסח בצורה שמשאירה בהם חורים. הפוסט הזה מניח שאתם כבר יודעים מה זה XSS ומה זה CSP באופן כללי, ומתמקד בשאלה המעניינת יותר - איך CSP שמוגדר לא נכון נעקף בפועל.
התצורה השגויה הכי נפוצה - unsafe-inline ותווים כלליים¶
הדרך הכי מהירה להרוג את היעילות של CSP היא להוסיף unsafe-inline ל-script-src. זה מאפשר הרצה של כל תג <script> שמוטמע ישירות בדף, ובעצם מבטל את ההגנה המרכזית נגד XSS - אם תוקף מצליח להזריק HTML, הוא יכול פשוט להזריק תג script רגיל והוא ירוץ. הבעיה היא שהרבה אתרים מוסיפים את זה כי הם משתמשים בקוד inline בכל מקום, ובמקום לתקן את הארכיטקטורה הם פשוט מרחיבים את המדיניות עד שהיא לא עושה כלום.
בעיה דומה קורית עם תווים כלליים רחבים מדי, כמו script-src *.example.com בלי מחשבה על מה בדיוק רץ תחת כל תת-דומיין. אם אחד מתת-הדומיינים האלה מריץ שירות שמאפשר להעלות תוכן משתמש - למשל שירות אחסון קבצים, או פורטל ישן שנשכח - תוקף יכול לארח שם קובץ JS זדוני, וה-CSP יאשר אותו בשמחה כי הוא "מהדומיין המורשה".
ניצול נקודות קצה מהימנות - JSONP ו-CDN¶
תוקפים חכמים לא מחפשים רק חורים בהגדרה עצמה, אלא גם דרכים לנצל דומיינים שכבר נמצאים ברשימת ההיתרים. שתי דוגמאות קלאסיות:
- אנדפוינטים של JSONP. אם דומיין ברשימת ההיתרים מריץ אנדפוינט JSONP ישן, שמחזיר קוד JavaScript שמכיל פרמטר שהתוקף שולט בו, אפשר לבנות כתובת שגורמת לאותו דומיין המהימן להחזיר בדיוק את הקוד שהתוקף רוצה להריץ. מבחינת ה-CSP, הבקשה יצאה מדומיין מורשה, אז היא עוברת חלק.
- קבצים ציבוריים על CDN מהימן. CDN גדולים ומוכרים מארחים לפעמים גם ספריות ישנות עם חולשות ידועות, או מאפשרים לכל אחד להעלות אליהם תוכן. אם דומיין ה-CDN כולו נמצא ברשימת ההיתרים בלי הגבלה לנתיב ספציפי, תוקף יכול למצוא שם קובץ שמריץ קוד לפי פרמטר, ולנצל אותו כדי לעקוף את ה-CSP.
הדפוס המשותף לשתי הדוגמאות הוא שהתוקף לא שובר את ה-CSP, הוא פשוט מוצא דרך להשתמש בדומיין שכבר קיבל אמון, בצורה שמי שהגדיר את המדיניות לא צפה.
מה קורה כש-object-src ו-base-uri חסרים¶
הרבה הגדרות CSP מתמקדות ב-script-src ושוכחות הנחיות חשובות נוספות. שתיים שחוזרות על עצמן:
- חוסר ב-object-src. בלי הגבלה על
object-src, אפשר להטמיע תגי<object>או<embed>שטוענים תוכן מסוגים ישנים יותר, שלפעמים מצליחים לעקוף את ההגנה שה-script-src נותן. - חוסר ב-base-uri. תג
<base>קובע את הכתובת הבסיסית שממנה נטענים כל הנתיבים היחסיים בדף. אםbase-uriלא מוגדר, תוקף שמצליח להזריק תג<base>יכול לשנות את הכתובת הבסיסית כך שסקריפטים שנטענים בנתיב יחסי - כאלה שנחשבים "בטוחים" כי הם מהדומיין עצמו - בפועל ייטענו משרת של התוקף.
ההנחיות האלה נראות שוליות, אבל הן בדיוק סוג הפרטים ש-CSP חזק צריך לכסות, ולא רק את החלק המובן מאליו.
אז אם אפשר לעקוף, האם CSP בכלל שווה משהו¶
התשובה הפשוטה היא כן, בהחלט. CSP לא נועד להיות חומה בלתי חדירה, הוא נועד להיות שכבת הגנה נוספת - defense in depth - שמצמצמת דרמטית את מרחב האפשרויות של תוקף גם כשקיימת חולשת XSS. מדיניות CSP קפדנית, בלי unsafe-inline, עם רשימת דומיינים מצומצמת ומדויקת, ועם כיסוי מלא של object-src ו-base-uri, הופכת ניצול מוצלח של XSS ממשימה קלה למשימה שדורשת מציאת חולשה נוספת וספציפית. זה בדיוק ההבדל בין הגנה שעוצרת רוב התקיפות לבין הגנה שנראית טוב רק במסמך.
אם אתם רוצים להעמיק בנושא ולעבור על כל התחום בצורה מסודרת ומעשית, מוזמנים לעבור על קורס פריצת אתרים מתקדמת.
ואם אתם מנתחים מדיניות CSP של אתר שאתם בודקים ולא בטוחים אם היא ניתנת לעקיפה, שווה לפתוח את זה לדיון בדיסקורד שלנו.
לסיכום¶
עקיפת CSP כמעט תמיד מתחילה מהגדרה רופפת מדי ולא מפגם במנגנון עצמו. תוקף טוב יודע לחפש unsafe-inline, תווים כלליים רחבים, נקודות קצה מהימנות שאפשר לנצל, והנחיות חסרות כמו object-src ו-base-uri. מגן טוב יודע שההגנה האמיתית נמצאת בדיוק בפרטים הקטנים האלה, ושהם שווים את המאמץ גם אם אף הגנה לא מושלמת ב-100 אחוז.