REST API של WordPress כולל את נקודת הקצה /wp-json/wp/v2/users. בברירת מחדל היא מחזירה לכל מבקר אנונימי את רשימת המשתמשים שיש להם פוסטים פורסמו – שם משתמש, slug, ID, ולעיתים גם תיאור. עבור תוקף, זה צעד ראשון מושלם בהתקפת brute-force: במקום לנחש גם שם משתמש וגם סיסמה, הוא צריך לנחש רק סיסמה.
למה זה משנה
תוקפים שלא יודעים מי האדמין באתר מתחילים מ-GET ל-/wp-json/wp/v2/users. תוך שנייה הם יודעים שמשתמש בשם geffen הוא ID 1 ולכן כמעט בוודאות אדמין. עכשיו הם מתחברים ל-wp-login.php עם השם הזה ועם רשימת 10,000 הסיסמאות הנפוצות ביותר. גם אם אתה משתמש ב-Cloudflare WAF, הוא לא חוסם את הניסיון הראשון – הוא רק מגביל קצב. הניחוש הראשון של "Password123!" כבר מגיע לשרת. ב-WordPress 4.7.1 נוספה סינון של משתמשים בלי פוסטים, אבל לא של משתמשים שכן פרסמו – ולכן כל בלוג עם שם הכותב גלוי בעמוד הבית כבר חושף לפחות אדמין אחד.
איך לזהות
גש בדפדפן (במצב גלישה פרטית, בלי להיות מחובר) לכתובת https://example.com/wp-json/wp/v2/users. אם אתה רואה JSON עם שמות משתמש – החשיפה פעילה. ניסיון נוסף: https://example.com/?rest_route=/wp/v2/users – זה עוקף תוספי אבטחה שמסננים רק לפי נתיב URL. הרבה תוספים כמו "Disable REST API" חוסמים את הצורה הראשונה אבל לא את השנייה.
איך לתקן
הדרך הנקייה: סינון rest_authentication_errors שמחזיר 401 לכל בקשה למשתמשים מצד מבקר לא מחובר. את התוסף Gutenberg ואת REST API הפנימיים זה לא שובר, כי המנהל המחובר עדיין יקבל את הנתונים.
// הוסף ל-mu-plugin או ל-functions.php של תבנית ילד
add_filter('rest_authentication_errors', function ($result) {
if (!empty($result)) {
return $result;
}
if (!is_user_logged_in()) {
global $wp;
$route = $wp->request ?? '';
if (preg_match('#wp/v2/users#', $route) || (isset($_GET['rest_route']) && strpos($_GET['rest_route'], 'wp/v2/users') !== false)) {
return new WP_Error('rest_login_required', 'Authentication required', ['status' => 401]);
}
}
return $result;
});
טכניקה משלימה: שנה את user_nicename כך שיהיה שונה מ-user_login. גם אם הסינון יישבר באיזשהו עדכון עתידי, ה-slug שייחשף לא יהיה שם ההתחברות:
UPDATE wp_users SET user_nicename = 'author-x9k2' WHERE user_login = 'admin';
טעויות נפוצות
טעות ראשונה: לחסום את /wp-json/ כולו ב-.htaccess. זה שובר את עורך הבלוקים, את Yoast SEO, את Contact Form 7 ועוד עשרות תוספים שמשתמשים ב-REST API באופן לגיטימי. הסינון חייב להיות סלקטיבי לנקודת הקצה users בלבד. טעות שנייה: לבדוק רק את הצורה /wp-json/wp/v2/users ולשכוח את ?rest_route=. תוקפים מודעים לעקיפה הזו. טעות שלישית: לסמוך על Yoast/RankMath – אף אחד מהם אינו מטפל ב-enumeration ברירת מחדל.
בדיקה לאחר תיקון
במצב גלישה פרטית בדוק את שתי הצורות. שתיהן צריכות להחזיר 401 עם JSON: {"code":"rest_login_required","message":"Authentication required","data":{"status":401}}. לאחר מכן התחבר כאדמין ובדוק שעורך הבלוקים, הוספת תוסף ושינוי הגדרות עדיין עובדים. לבסוף, פתח את האודיט ב-RankPlus וודא שהבדיקה rest_users_public עוברת.
?author=N – שתי החסימות מטפלות בשני וקטורים שונים של אותה התקפה.