תארו לעצמכם: חנות מקוונת שלכם מקבלת ביקורות, ודרך שדה התגובה תוקף מזריק סקריפט ששולח את העוגיות של כל המבקרים לשרת שלו. או שמערכת ניהול התוכן שלכם שומרת מאמר עם JavaScript זדוני בגוף העמוד, וכל מי שפותח אותו מסתכן באובדן הסשן שלו. לפי OWASP Top 10, XSS נמצא בין שלוש הפגיעויות הקריטיות ביותר ביישומי אינטרנט, כשהנזק הממוצע מהתקפה בודדת מוערך ב-$20,000. יש לנו ניסיון של למעלה מ-5 שנים באבטחה וביצענו יותר מ-50 ביקורות. המשימה שלנו היא לבנות הגנה רב-שכבתית: מסינון קלט ועד הגדרת כותרות אבטחה. הזמינו ביקורת—נכין דוח מפורט ומפת דרכים לתיקון.
מה זה XSS ואילו סוגים קיימים?
XSS (Cross-Site Scripting) הוא התקפה שבה תוקף מזריק JavaScript זדוני לעמוד. שלושה סוגים עיקריים:
- Reflected XSS—ה-payload מועבר דרך פרמטרי URL ומוחזר מיד לעמוד. דוגמה:
https://example.com/search?q=<script>alert(document.cookie)</script>. - Stored XSS—ה-payload נשמר במסד הנתונים (תגובות, פרופיל משתמש) ומופעל על ידי כל מי שצופה בעמוד.
- DOM-based XSS—ה-payload מעובד על ידי JavaScript בצד הלקוח ללא מעורבות השרת. מסוכן כי מסנני צד שרת לא יכולים לזהות אותו.
כל סוג דורש גישת הגנה משלו. פרטים נוספים: Cross-site scripting.
איך פועל escape של פלט?
כלי ההגנה העיקרי הוא escape תלוי-הקשר בעת פלט נתונים. בואו נסתכל על טכנולוגיות פופולריות.
// PHP/Blade (Laravel) — автоматическое экранирование
{{ $userInput }} {{-- & < > " ' --}}
{!! $trustedHtml !!} {{-- только для доверенного контента --}}
// Безопасная установка cookie
setcookie('session', $value, [
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
// Валидация на входе (Laravel)
$validated = $request->validate([
'name' => 'required|string|max:255|regex:/^[a-zA-Zа-яёА-ЯЁ\s\-]+$/u',
'website' => 'nullable|url',
'comment' => 'required|string|max:5000',
]); // React — JSX экранирует по умолчанию
<div>{userInput}</div>
// Опасно — только с санированным HTML
<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />
// Vue — автоматическое экранирование
<span>{{ userInput }}</span>
// Опасно — v-html без санизации
<span v-html="userInput"></span>
// DOMPurify — санация для WYSIWYG
import DOMPurify from 'dompurify';
const cleanHtml = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li'],
ALLOWED_ATTR: ['href', 'target'],
ALLOW_DATA_ATTR: false,
});
# Nginx — заголовки безопасности
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
# Флаги для Set-Cookie
proxy_cookie_path / "/; HttpOnly; Secure; SameSite=Strict"; סינון תוכן HTML
אם משתמשים יכולים להזין טקסט מעוצב (עורכי WYSIWYG), יש צורך בספריית סינון. בשרת (PHP) אנו משתמשים ב-HTMLPurifier:
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'b,i,em,strong,a[href|title],p,ul,li');
$purifier = new HTMLPurifier($config);
$clean = $purifier->purify($userInput);DOMPurify בצד הלקוח חוסם 95% מאיומי XSS לעומת 60% במסננים מותאמים אישית. אנו משתמשים בשתי הגישות להגנה מרבית.
מה זה CSP ואיך הוא עובד?
CSP (Content Security Policy) הוא כותרת HTTP שמונעת מהדפדפן להריץ סקריפטים לא מורשים. אנו מגדירים את המדיניות לפרויקט שלכם: מאפשרים רק דומיינים משלכם, חוסמים // PHP/Blade (Laravel) — автоматическое экранирование {{ $userInput }} {{-- & < > " ' --}} {!! $trustedHtml !!} {{-- только для доверенного контента --}} // Безопасная установка cookie setcookie('session', $value, [ 'httponly' => true, 'secure' => true, 'samesite' => 'Strict' ]); // Валидация на входе (Laravel) $validated = $request->validate([ 'name' => 'required|string|max:255|regex:/^[a-zA-Zа-яёА-ЯЁ\s\-]+$/u', 'website' => 'nullable|url', 'comment' => 'required|string|max:5000', ]); , אוסרים סקריפטים מוטבעים. זה מפחית את הסיכון ל-DOM-based XSS לאפס. בפרויקט אחד, CSP מנע התקפה כשהתוקף הצליח להזריק סקריפט דרך ווידג'ט של צד שלישי—המדיניות פשוט חסמה את ההרצה שלו. בשילוב עם escape של פלט, CSP מספק 99% הגנה לעומת 70% כשמשתמשים רק ב-escape. פרטים נוספים: Content Security Policy.
למה escape של פלט לא מספיק? תבניות DOM מסוכנות
Escape של פלט הוא הגנה בסיסית, אבל הוא לא מגן מפני DOM-based XSS כשהפגיעות נמצאת בקוד צד לקוח. לדוגמה, תבניות עם // React — JSX экранирует по умолчанию <div>{userInput}</div> // Опасно — только с санированным HTML <div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} /> // Vue — автоматическое экранирование <span>{{ userInput }}</span> // Опасно — v-html без санизации <span v-html="userInput"></span> // DOMPurify — санация для WYSIWYG import DOMPurify from 'dompurify'; const cleanHtml = DOMPurify.sanitize(userInput, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li'], ALLOWED_ATTR: ['href', 'target'], ALLOW_DATA_ATTR: false, }); , # Nginx — заголовки безопасности add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; # Флаги для Set-Cookie proxy_cookie_path / "/; HttpOnly; Secure; SameSite=Strict"; , $config = HTMLPurifier_Config::createDefault(); $config->set('HTML.Allowed', 'b,i,em,strong,a[href|title],p,ul,li'); $purifier = new HTMLPurifier($config); $clean = $purifier->purify($userInput); עם ארגומנטים מסוג מחרוזת נשארות מסוכנות. לכן, אנו תמיד משתמשים בגישה מקיפה: escape, סינון קלט ו-CSP.
// Опасно
document.getElementById('output').innerHTML = location.hash.slice(1);
eval(userData);
setTimeout(userCallback, 1000); // если userCallback — строка
// Безопасно
document.getElementById('output').textContent = location.hash.slice(1);
תשומת לב מיוחדת ל: eval(), innerHTML, eval, setTimeout, // Опасно document.getElementById('output').innerHTML = location.hash.slice(1); eval(userData); setTimeout(userCallback, 1000); // если userCallback — строка // Безопасно document.getElementById('output').textContent = location.hash.slice(1); , innerHTML עם ארגומנטים מסוג מחרוזת.
איך לבדוק DOM-based XSS?
השתמשו בסורקים ובבדיקות ידניות:
- OWASP ZAP — סורק אוטומטי
- Burp Suite Community — בדיקות ידניות
- DOM XSS Scanner — תוסף דפדפן
- ב-DevTools של הדפדפן: לשונית Security, בדיקת כותרות CSP
אנו גם מבצעים סקירת קוד והרצת ניתוח דינמי. זה מאפשר לזהות עד 95% מהפגיעויות לפני העלייה לייצור.
השוואת שיטות הגנה
| שיטה | רמה | כיסוי | מורכבות יישום |
|---|---|---|---|
| Escape של פלט | בסיסית | חל על הפלט | נמוכה |
| סינון קלט | בינונית | רק תוכן HTML | בינונית |
| CSP | מתקדמת | כל תוכן העמוד | גבוהה (דורש כוונון) |
| עוגיית HttpOnly | בסיסית | רק עוגיות | נמוכה |
בפועל, השילוב של escape + CSP + עוגיית HttpOnly סוגר 99% מווקטורי XSS. ה-1% הנותרים הם בדרך כלל zero-days בדפדפן, אותם אנו עוקבים באמצעות ניטור.
מה כוללת העבודה והשלבים
מקרה לדוגמה: חנות מקוונת עם ביקורות
הלקוח—קמעונאי גדול עם מנוע PHP מותאם אישית. תוקף הזריק Stored XSS בשדה הביקורת, וגנב סשנים של מנהלים. לאחר הביקורת שלנו: החלפנו את `echo $comment` ב-escape בתבנית, יישמנו HTMLPurifier והגדרנו CSP. ההתקפות נפסקו, וזמן טעינת העמוד נותר ללא שינוי.| שלב | משך | תיאור |
|---|---|---|
| ביקורת | 2–4 ימים | בדיקת כל נקודות הקלט/פלט, חיפוש תבניות מסוכנות (innerHTML, eval, setTimeout עם מחרוזת) |
| תיקון | 3–7 ימים | החלפת פונקציות מסוכנות, יישום ספריות סינון |
| הגדרת CSP | 2–4 ימים | פיתוח מדיניות, בדיקות, רישום שגיאות |
| תיעוד והדרכה | 1–2 ימים | תיאור הצעדים, הוראות למפתחים |
| תמיכה לאחר ביקורת | רבעוני | ניטור לוגים, עדכוני מדיניות CSP |
צרו קשר לייעוץ—נעריך את הפרויקט שלכם ונציע תוכנית הגנה אופטימלית. הזמינו ביקורת היום וקבלו דוח מפורט עם מפת דרכים לתיקון. אנו מבטיחים הפחתת ההסתברות להתקפת XSS מוצלחת לפחות מ-1%.







