Content-Security-Policy חסרה - מה זה ואיך מתקנים
Content-Security-Policy (CSP) היא הכותרת שאומרת לדפדפן מאילו מקורות מותר לטעון סקריפטים, סגנונות ותמונות, ולאן מותר לדף לשלוח נתונים. בלעדיה הדפדפן מריץ כל סקריפט שמגיע לדף - וזו הבדיקה שנכשלת הכי הרבה בסריקות שלנו.
למה זה מסוכן
CSP היא רשת הביטחון נגד XSS: גם כשתוקף מצליח להזריק סקריפט לדף - דרך פרמטר בכתובת, תוכן שמשתמשים מזינים או ספרייה חיצונית שנפרצה - מדיניות טובה חוסמת את ההרצה או את שליחת הנתונים החוצה. בלי CSP הסקריפט המוזרק רץ חופשי: גונב טוקנים, קורא טפסים ופועל בשם המשתמש.
מה זה CSP במילים פשוטות
תחשוב על הדפדפן כעל סדרן בכניסה למסיבה, ועל CSP כעל רשימת האורחים שאתה נותן לו. בלי רשימה, הסדרן מכניס כל אחד שמגיע ואומר שהוא הוזמן. עם רשימה, נכנסים רק מי שכתוב בה.
בפועל הרשימה הזו היא שורת טקסט אחת שהשרת מחזיר יחד עם הדף, והיא אומרת לדפדפן מאילו כתובות מותר לטעון סקריפטים, עיצוב, תמונות ופונטים, ולאן מותר לדף לשלוח נתונים. כל מה שלא ברשימה - הדפדפן חוסם בעצמו, עוד לפני שהקוד רץ.
איך נראית מדיניות אמיתית
זו מדיניות סבירה לאתר טיפוסי שמשתמש ב-Google Fonts ובאנליטיקס:
Content-Security-Policy: default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; object-src 'none'
מה כל חלק אומר:
default-src 'self'- ברירת המחדל לכל סוגי המשאבים: רק מהדומיין שלי. כל שורה אחרי זה היא חריגה מפורשת.script-src- מאיפה מותר לטעון JavaScript. זו השורה הכי חשובה, כי היא זו שחוסמת XSS.style-src- מאיפה מותר לטעון עיצוב.'unsafe-inline'כאן נפוץ ונחשב סביר, כי CSS מוזרק הרבה פחות מסוכן מקוד.connect-src- לאן הדף רשאי לשלוח בקשות ברקע. זו השורה שמונעת מסקריפט מוזרק לשלוח את הנתונים שגנב לשרת של התוקף.frame-ancestors 'none'- אף אתר לא יכול לעטוף אותך ב-iframe. זו ההגנה מפני clickjacking, והיא מחליפה את X-Frame-Options.object-src 'none'- חוסם תוספים ישנים כמו Flash. אין סיבה להשאיר את זה פתוח.
השגיאות שתראה בקונסול ומה הן אומרות
אחרי שמוסיפים CSP, הדבר הראשון שקורה הוא שמשהו נשבר ומופיעה שגיאה אדומה בקונסול. זה לא באג - זו המדיניות עובדת. השגיאה תמיד אומרת בדיוק מה נחסם ואיזו הוראה חסמה אותו:
Refused to load the script ... because it violates the following Content Security Policy directive: "script-src 'self'"
התרגום: הדף ניסה לטעון סקריפט מכתובת חיצונית, ו-script-src מרשה רק את הדומיין שלך. הפתרון הוא להוסיף את הכתובת הזו ל-script-src - בתנאי שאתה מזהה אותה ויודע למה היא שם.
Refused to execute inline script because it violates the following Content Security Policy directive
התרגום: יש <script> עם קוד ישירות בתוך ה-HTML. או שמעבירים אותו לקובץ נפרד, או שנותנים לו nonce.
Refused to connect to ... because it violates the following Content Security Policy directive: "connect-src 'self'"
התרגום: הקוד מנסה לפנות ל-API חיצוני שלא הרשית - למשל Supabase או שירות תשלומים. מוסיפים את הדומיין שלו ל-connect-src.
הכלל: אל תשתיק שגיאה על ידי הרחבת המדיניות לפני שהבנת מה בדיוק נחסם. הרבה פעמים הדבר שנחסם הוא בדיוק מה שלא היה אמור לרוץ.
למה תג meta הוא לא תחליף לכותרת
זו הטעות הכי נפוצה שאנחנו רואים. אפשר להגדיר CSP גם כתג <meta http-equiv="Content-Security-Policy"> בתוך ה-HTML, וזה נראה כאילו זה עובד - אבל זה תחליף חלקי בלבד:
- הוראות מרכזיות פשוט לא נתמכות בתג meta, ביניהן
frame-ancestors(ההגנה מפני clickjacking) ו-report-uri. - התג חל רק על מסמכי HTML שהוא יושב בהם. כותרת HTTP חלה על כל תגובה שהשרת מחזיר.
- התג נקרא רק כשהדפדפן מגיע אליו בזמן פענוח הדף, כך שמשאבים שנטענו לפניו כבר יצאו לדרך.
לכן ההגדרה הנכונה היא תמיד בשכבת ההגשה: vercel.json ב-Vercel, netlify.toml או קובץ _headers ב-Netlify, headers() ב-Next.js, או helmet() בשרת Express.
CSP באפליקציה שנבנתה בכלי AI
כאן נמצא הפער האמיתי. כלי כמו Lovable, Bolt או v0 מייצרים אפליקציה שעובדת מצוין, אבל אף אחד לא ביקש מהם להגדיר כותרות אבטחה - ולכן הן פשוט לא שם. בסריקות שלנו זו הבדיקה שנכשלת הכי הרבה.
שתי נקודות שחשוב לדעת לפני שמוסיפים CSP לאפליקציה כזו:
- הקוד שנוצר אוטומטית נוטה להשתמש בסקריפטים וסגנונות inline. מדיניות נוקשה תשבור את האפליקציה בטעינה הראשונה. לכן מתחילים תמיד ב-
Content-Security-Policy-Report-Only, שרק מדווח בקונסול בלי לחסום, ועוברים לאכיפה רק אחרי שהקונסול נקי. - האפליקציה כמעט תמיד מדברת עם שירותים חיצוניים - Supabase, ספק תשלומים, מודל AI. כל אחד מהם צריך להופיע ב-
connect-src, אחרת האפליקציה תיראה תקינה אבל תפסיק לשמור נתונים.
אם אתה לא בטוח מאילו מקורות האפליקציה שלך באמת טוענת, אל תנחש: הרץ אותה שבוע ב-Report-Only, אסוף את הרשימה מהקונסול, ורק אז כתוב את המדיניות הסופית.
איך מתקנים
- הגדר CSP ככותרת HTTP בשכבת ההגשה - תג meta הוא תחליף חלקי בלבד, ובלעדיו ההגנה לא חלה על כל התגובות
- התחל מבסיס צר:
default-src 'self', והוסף במפורש רק את המקורות שהאתר באמת טוען מהם (פונטים, אנליטיקס, תשלומים) - לפני אכיפה, הרץ שבוע במצב
Content-Security-Policy-Report-Only- רואים בקונסול מה היה נחסם בלי לשבור כלום - הימנע מ-
'unsafe-inline'ב-script-src - הוא מבטל את עיקר ההגנה; העבר סקריפטים inline לקבצים או השתמש ב-nonce
איך מוודאים שהתיקון עבד
הרץ curl -sSI https://your-domain.com וודא ש-Content-Security-Policy מופיעה, פתח את האתר וודא שאין שגיאות CSP בקונסול, ואז הרץ סריקה חוזרת.
מדריכים לפי הכלי שבנה את האפליקציה
- Content-Security-Policy חסרה באפליקציית Lovable
- Content-Security-Policy חסרה באפליקציית Base44
- Content-Security-Policy חסרה באפליקציית Bolt
- Content-Security-Policy חסרה באפליקציית Cursor
- Content-Security-Policy חסרה באפליקציית v0
- Content-Security-Policy חסרה באפליקציית Replit
- Content-Security-Policy חסרה באפליקציית Windsurf
בדיקה חינם
רוצה לדעת אם האפליקציה שלך חשופה? סרוק בחינם ב-30 שניות וקבל ציון אבטחה + הוראות תיקון.