דגלי עוגיות סשן – Secure, HttpOnly, SameSite

בלי שלושת הדגלים האלה, סשן מנהל יכול להיגנב דרך XSS, ציתות HTTP או CSRF. ככה מגדירים נכון.

עוגיות סשן ב-WordPress (כולל wordpress_logged_in_*, wordpress_sec_* ועוגיות PHP גנריות) הן בעצם המפתח לחשבון. כל מי שתופס את ערך העוגייה הופך לבעל החשבון – בלי סיסמה, בלי 2FA. שלושת הדגלים Secure, HttpOnly ו-SameSite נועדו להקשות על גניבה של אותה עוגייה. בלי אחד מהם, נשארת דרך פתוחה.

למה זה משנה

בלי Secure, הדפדפן ישלח את העוגייה גם בבקשת HTTP – מבקר על Wi-Fi של בית קפה שגולש קודם ל-http://example.com חושף את ערך העוגייה לכל מי שמאזין לרשת. דאי ל-sslstrip כדי לשבור הצפנה אם הדפדפן ניאות לשלוח. בלי HttpOnly, JavaScript יכול לקרוא את העוגייה דרך document.cookie. אם תוסף או theme פגיעים ל-XSS (גם reflected XSS קל בעמוד 404), כל סקריפט שתוקף יזריק יוכל לאסוף את עוגיית האדמין ולשלוח אותה לשרת חיצוני. בלי SameSite, אתר זדוני שאליו האדמין נכנס יכול לשלוח בקשת POST ל-wp-admin/admin-post.php שלך – הדפדפן יצרף את עוגיית הסשן אוטומטית, וה-CSRF מתבצע למרות nonce.

איך לזהות

פתח Chrome DevTools > Application > Cookies > בחר את הדומיין שלך. בדוק שלכל עוגייה (במיוחד wordpress_logged_in_* ו-PHPSESSID) יש סימון בעמודות Secure ו-HttpOnly, וערך Lax או Strict תחת SameSite. בדיקה משלימה ב-shell:

curl -I -c /tmp/cookies https://example.com/wp-login.php
grep -E "Set-Cookie|HttpOnly|Secure|SameSite" /tmp/cookies

איך לתקן

הוסף ל-wp-config.php, לפני require_once ABSPATH . 'wp-settings.php';:

// אכוף Secure על עוגיות WP
define('COOKIE_SECURE', true);
define('FORCE_SSL_ADMIN', true);

// PHP session cookies (אם משתמשים ב-PHP sessions)
@ini_set('session.cookie_secure', 1);
@ini_set('session.cookie_httponly', 1);
@ini_set('session.cookie_samesite', 'Lax');
@ini_set('session.use_only_cookies', 1);

WordPress עצמו מסמן את עוגיות ה-auth כ-HttpOnly מאז גרסה 2.5, אבל לא תמיד מסמן Secure – לכן COOKIE_SECURE חיוני. לטיפול ברמת השרת (כש-PHP-FPM לא מציית ל-ini_set):

# ב-.htaccess בשורש האתר
<IfModule mod_headers.c>
    Header edit Set-Cookie ^(.*)$ "$1; HttpOnly; Secure; SameSite=Lax"
</IfModule>
# ב-Nginx
proxy_cookie_path / "/; HTTPOnly; Secure; SameSite=Lax";

טעויות נפוצות

טעות ראשונה: להגדיר SameSite=Strict באתר עם תהליך checkout שמפנה ל-PayPal/Stripe ובחזרה. Strict לא ישלח את העוגייה כשהמשתמש חוזר מהתשלום, והוא ייצא מהחשבון. Lax כמעט תמיד הבחירה הנכונה. טעות שנייה: להגדיר רק את ה-php.ini אבל לשכוח COOKIE_SECURE – עוגיות WP-AUTH לא מושפעות מהגדרות PHP. טעות שלישית: להפעיל FORCE_SSL_ADMIN כשעדיין יש מסלולים פנימיים שעובדים על HTTP – זה ייצור לולאות redirect.

בדיקה לאחר תיקון

התנתק וההתחבר מחדש כך ש-WordPress יכתוב עוגיות חדשות. ב-DevTools > Application > Cookies בדוק שלכל עוגייה יש Secure=true, HttpOnly=true, SameSite=Lax. הרץ שוב את האודיט. בנוסף, בדוק ב-https://www.cookieserve.com או ב-cookiestatus.com שהדפדפן מקבל את העוגיות עם הדגלים הצפויים.

גם חשוב לבדוק שהאתר לא משתמש ב-localStorage או sessionStorage לשמירת tokens – הם נגישים ב-JavaScript ולא מוגנים על ידי הדגלים שדנו בהם. אם תוסף מאחסן JWT ב-localStorage, אותה פגיעות XSS שדגל HttpOnly מונע על cookie תאפשר גניבת ה-token. בדוק ב-DevTools > Application > Local Storage שלא מאוחסנים שם נתוני אימות. אם כן – זה תוסף שצריך להחליף, או לפנות לפיתוח התוסף לבקש שינוי ל-HttpOnly cookie. הגנה אמיתית דורשת מבט הוליסטי על כל מקורות האחסון של פרטי סשן בדפדפן.

טיפ: אם משהו נשבר אחרי הפעלת SameSite=Lax (למשל פתיחת WordPress דרך iframe באתר אחר), שקול SameSite=None; Secure – זה מאפשר שילוב cross-site אבל דורש HTTPS.