כותרות אבטחה הן הוראות ש-WordPress – או ליתר דיוק, השרת – שולח לדפדפן בכל תגובת HTTP. הן לא מתקנות פגיעות בקוד, אבל הן מצמצמות באופן דרמטי את היכולת של תוקף לנצל פגיעות שכן קיימת. הוספתן עולה אפס – רק שינוי קונפיג בשרת – ומונעת קטגוריות שלמות של התקפות לקוח.
למה זה משנה
בלי X-Frame-Options תוקף יכול להטמיע את האתר שלך בתוך iframe באתר זדוני, להציג מעליו כפתורים שקופים, ולגרום למשתמש מחובר ללחוץ על "מחק חשבון" בלי לדעת – זה נקרא clickjacking. בלי X-Content-Type-Options: nosniff, דפדפנים "מנחשים" סוג קובץ – ולכן תמונה שתוקף העלה ובתוכה JavaScript עשויה לרוץ כסקריפט. בלי Referrer-Policy מתאים, מבקר שיגיע אליך מ-/wp-admin/edit.php?post_id=42 ילחץ על קישור חיצוני וייקח אתו את הנתיב הפנימי – חושף מבנה אדמין. בלי Permissions-Policy כל סקריפט באתר יכול לבקש גישה למצלמה, מיקרופון או GPS. בלי CSP כל פרצת XSS הופכת מ"חולשה תיאורטית" ל"קוד שזורם בדפדפן של כל מבקר".
איך לזהות
הרץ:
curl -I https://example.com/ | grep -iE "x-frame|x-content|referrer|permissions|content-security"
אם השורות חסרות – הכותרות לא מוגדרות. כלי חיצוני נוח: securityheaders.com – ייתן ציון A עד F ופירוט מה חסר. אתר WordPress טיפוסי מקבל F עד שמתקנים.
איך לתקן
ב-Apache, הוסף ל-.htaccess בשורש או ל-VirtualHost:
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>
ב-Nginx, ב-server block:
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
CSP מורכב יותר. התחל ב-Report-Only כדי לראות מה ייחסם בלי לשבור את האתר:
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; report-uri /csp-report"
טעויות נפוצות
טעות ראשונה: לכפות CSP מחמיר ביום הראשון. WordPress וערכות נושא רבות משתמשות ב-inline JavaScript ו-inline CSS. CSP בלי 'unsafe-inline' ישבר ערכת נושא טיפוסית. עובדים בהדרגה: Report-Only שבועיים, אז אוכפים. טעות שנייה: להשתמש ב-X-Frame-Options: DENY כשיש Elementor, page builder, או iframe פנימי. SAMEORIGIN כמעט תמיד הבחירה הנכונה. טעות שלישית: להוסיף את הכותרות גם בקובץ קונפיג של Nginx וגם דרך תוסף PHP – זה יוצר כפילות שגורמת ל-X-Frame-Options: SAMEORIGIN, SAMEORIGIN שדפדפנים לעיתים מתעלמים ממנה.
בדיקה לאחר תיקון
הרץ curl -I כמו למעלה ואשר שכל הכותרות מופיעות. גש ל-securityheaders.com ובדוק שאתה ב-A או A+. עבור על כמה דפים בכרום DevTools > Network > Headers ובדוק שהכותרות יורדות בכל בקשה. אם הוספת CSP, פתח את ה-Console ובדוק שאין שגיאות "Refused to load" – אם יש, חזור ל-Report-Only עד שתסדר.
בנוסף, חשוב לזכור ש-CDN כמו Cloudflare מאפשר להוסיף את כל הכותרות בלחיצה אחת דרך "Transform Rules" או "HTTP Response Header Modification". זה מהיר מאוד להגדרה ומונע צורך לערוך קבצי שרת. החיסרון: התלות ב-CDN; אם תעבור ספק או יהיה outage, ההגנה תיעלם. עדיף להחזיק את הכותרות ברמת השרת כברירת מחדל, ולהוסיף ב-CDN רק כשכבה משלימה.
.htaccess או ל-Nginx, עדיפה ההגדרה ברמת השרת – חינמית, מהירה יותר, ולא תלויה ב-PHP.