פיתוח ותוכנה מדריך

Content Security Policy בלי לשבור את האתר: מתחילים ב-Report-Only

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

מגן גיאומטרי מורכב מריבועים, חלקם מלאים בסגול וחלקם ריקים

Content Security Policy (CSP) היא כותרת HTTP שהשרת שולח עם כל עמוד, ובה רשימה: מאיפה מותר לטעון סקריפטים, סגנונות, תמונות, גופנים ומסגרות. דפדפן שמקבל אותה מסרב לטעון כל דבר שלא ברשימה, כולל סקריפט שתוקף הצליח להזריק דרך תגובה או שדה טופס. זו אחת ההגנות היעילות נגד XSS, וגם אחת הדרכים הקלות ביותר להשבית אתר בטעות.

הכלל הראשון: לא לאכוף ביום הראשון

יש שתי כותרות. Content-Security-Policy אוכפת. Content-Security-Policy-Report-Only לא חוסמת כלום, רק שולחת דיווח על כל מה שהיה נחסם. מתחילים תמיד מהשנייה. היא מגלה לכם כל סקריפט, תמונה ו-iframe שלא זכרתם, בלי שאף גולש ייפגע.

כך נראית מדיניות ראשונה, מחמירה בכוונה:

Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; report-to csp-endpoint
Reporting-Endpoints: csp-endpoint="https://your-site.co.il/csp-report"

'self' פירושו ״מהדומיין של העמוד בלבד״. הכותרת Reporting-Endpoints מגדירה לאן ישלחו הדיווחים; ההנחיה הישנה report-uri מסומנת ב-MDN כמיושנת, אך עדיין מומלץ לציין את שתיהן עד שכל הדפדפנים תומכים ב-report-to.

הכלל השני: לקרוא את הדיווחים

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

הבעיה האמיתית: סקריפטים inline

רוב האתרים הקיימים כוללים JavaScript בתוך ה-HTML: <script> עם קוד, onclick בתגים, קוד שתוסף הדביק. מדיניות 'self' חוסמת את כולם, כי אין להם ״מקור״. הפתרון הגרוע הוא 'unsafe-inline', שמבטל את רוב ההגנה. שני פתרונות טובים:

  • nonce: השרת מייצר בכל תגובה מחרוזת אקראית, מצרף אותה לכותרת (script-src 'nonce-R4nd0m') ולכל תג סקריפט לגיטימי (<script nonce="R4nd0m">). תוקף שמזריק סקריפט לא יודע את ה-nonce של התגובה הזאת. חשוב: ערך חדש בכל תגובה, לא קבוע.
  • hash: לסקריפט שלא משתנה, מציינים את ה-SHA-256 של תוכנו. מתאים לקוד קטן וקבוע, למשל הסקריפט שמחיל התאמות נגישות לפני הציור הראשון.

ולסקריפטים שטוענים סקריפטים אחרים (מנהלי תגים, ספריות), 'strict-dynamic' אומר לדפדפן: סקריפט שאושר ב-nonce או ב-hash רשאי לטעון עוד סקריפטים, גם בלי nonce. זה מה שמאפשר מדיניות קפדנית בלי לתחזק רשימת דומיינים אינסופית.

סדר עבודה שעובד

  1. הוסיפו Report-Only עם default-src 'self' ואיסוף דיווחים. חכו שבוע.
  2. עברו על הדיווחים. לכל מקור חיצוני החליטו: להוסיף לרשימה, להעביר לאחסון שלכם, או להסיר.
  3. הוסיפו nonce לסקריפטים שלכם. בתבנית PHP זה אומר: לייצר ערך פעם אחת בתחילת הבקשה, ולהדפיס אותו בכותרת ובכל תג.
  4. הדקו: script-src 'nonce-…' 'strict-dynamic'; object-src 'none'; base-uri 'self'. שתי האחרונות סוגרות דרכי עקיפה ותיקות.
  5. כשהדיווחים שקטים במשך שבוע, שנו את שם הכותרת ל-Content-Security-Policy. השאירו את Report-Only במקביל עם מדיניות מחמירה יותר, כדי להתכונן לשלב הבא.

מה CSP לא פותרת

היא לא מונעת הזרקה; היא מונעת מההזרקה לרוץ. סינון קלט ובריחת פלט (escaping) נשארים חובה. היא גם לא עוזרת נגד סקריפט לגיטימי שנפרץ בצד הספק שלו: אם ספריית צד שלישי שאישרתם משתנה, הדפדפן יריץ אותה. לזה נועד integrity (SRI) על תגי סקריפט חיצוניים, ועדיף פשוט לארח את הספריות אצלכם.

ומילה על אתר על אחסון משותף: את הכותרות אפשר לשלוח גם מ-.htaccess (Header set Content-Security-Policy-Report-Only "…"), אבל nonce דורש קוד שרץ בכל בקשה, ולכן הוא שייך ל-PHP ולא לקובץ הגדרות סטטי.

המשך קריאה

פיתוח ותוכנה 1 דק׳ קריאה

הבאג הנדיר במטמון של Hyperdrive

תחקיר על הבאג הנדיר במטמון של Hyperdrive במהלך הבטא של תמיכת MySQL: מדוע תשובות ריקות הופיעו רק כששני רכיבים פירשו אותו מטמון אחרת, ואיך תיק…

מערכת BestSites

סוגריים מסולסלים גדולים עם סימן ביקורת ואזהרה בתוכם, בסגול וורוד

פיתוח ותוכנה ניתוח 3 דק׳ קריאה

קוד שנכתב עם AI: מה בודקים לפני שמעלים אותו לאתר

כלי AI כותבים קוד שעובד בהדגמה ומתפספס בפרטים שלא רואים: קלט שלא סונן, שאילתה שלא הוכנה מראש, תלות שלא קיימת. הבעיה אינה שהקוד גרוע, אלא שהו…

מערכת BestSites