Как обезопасить форму авторизации WordPress: от переноса до двухфакторной аутентификации

Раздел: Администрирование WordPress -> Авторизация и безопасность

Основные подходы к защите страницы входа

Как скрыть стандартный URL входа от злоумышленников?

Наиболее эффективное решение - изменение адреса wp-login.php. Используйте плагин WPS Hide Login или добавьте код в functions.php. После этого стандартный адрес перестанет открывать форму входа, что снижает риск атак.


// Изменение URL страницы входа
add_filter('site_url', 'custom_hide_login_url', 10, 3);
add_filter('network_site_url', 'custom_hide_login_url', 10, 3);
function custom_hide_login_url($url) {
    if (strpos($url, 'wp-login.php') !== false) {
        $url = str_replace('wp-login.php', 'moy-kabinet', $url);
    }
    return $url;
}

Wp login php 1 (страница входа wordpress)

Также перенаправьте старый адрес на 404 или главную страницу с помощью .htaccess:


# Перенаправление wp-login.php на 404
Redirect 404 /wp-login.php

Возможные проблемы:

  • Кэш плагинов может хранить старые ссылки - требуется очистка кэша.
  • Пользователи, запомнившие старый адрес, не смогут войти без редиректа.
  • Плагины, напрямую обращающиеся к wp-login.php, могут перестать работать.
  • В некоторых средах (например, с включённым mod_rewrite) .htaccess может не сработать - нужно проверить настройки сервера.

1. Как добавить проверку капчи на форму входа?

Установите плагин Google reCAPTCHA for WordPress или вставьте код в functions.php. Пример интеграции Google reCAPTCHA v2:


// Добавление капчи в форму входа
add_action('login_enqueue_scripts', 'add_recaptcha_script');
function add_recaptcha_script() {
    wp_enqueue_script('recaptcha', 'https://www.google.com/recaptcha/api.js', array(), null, true);
}
add_action('login_form', 'add_recaptcha_field');
function add_recaptcha_field() {
    echo '
'; } add_filter('authenticate', 'verify_recaptcha', 10, 3); function verify_recaptcha($user, $username, $password) { if (!isset($_POST['g-recaptcha-response'])) { return new WP_Error('captcha_error', 'Заполните капчу.'); } $response = wp_remote_get('https://www.google.com/recaptcha/api/siteverify?secret=СЕКРЕТ&response=' . $_POST['g-recaptcha-response']); $body = json_decode(wp_remote_retrieve_body($response)); if (!$body->success) { return new WP_Error('captcha_fail', 'Проверка капчи не пройдена.'); } return $user; }

Возможные проблемы:

  • Ключи рекапчи (sitekey и secret) должны быть получены на https://www.google.com/recaptcha.
  • Если капча не отображается, проверьте, что скрипт подключается только на странице входа (без лишних условий).
  • При включении кэширования капча может не прогружаться на кэшированной странице - исключите страницу входа из кэша.
  • Ошибка сети или недоступность серверов Google приведёт к блокировке входа - предусмотрите запасной вариант (например, пропускать проверку при ошибке).

2. Как ограничить количество неудачных попыток входа?

Плагин Limit Login Attempts Reloaded - самый простой путь. Вручную используйте транзиенты WordPress:


// Пример ограничения попыток
add_filter('authenticate', 'check_login_attempts', 30, 3);
function check_login_attempts($user, $username, $password) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $key = 'login_attempts_' . $ip;
    $attempts = (int) get_transient($key);
    if ($attempts >= 3) {
        return new WP_Error('too_many_attempts', 'Слишком много попыток. Подождите 5 минут.');
    }
    return $user;
}
add_action('wp_login_failed', 'increment_attempts');
function increment_attempts() {
    $ip = $_SERVER['REMOTE_ADDR'];
    $key = 'login_attempts_' . $ip;
    $attempts = (int) get_transient($key);
    set_transient($key, $attempts + 1, 300); // 5 минут
}
add_action('wp_login', 'clear_attempts');
function clear_attempts() {
    $ip = $_SERVER['REMOTE_ADDR'];
    delete_transient('login_attempts_' . $ip);
}

Возможные проблемы:

  • При использовании обратного прокси или Cloudflare IP может быть неверным - проверяйте заголовок HTTP_X_FORWARDED_FOR.
  • Лимит попыток может сбрасываться неправильно, если пользователь закрыл браузер - используйте более надёжные хранилища (база данных).
  • Сообщение об ошибке может быть неинформативным - настройте текст через $error_message.

3. Как внедрить двухфакторную аутентификацию?

Используйте плагин Two Factor от WordPress.org или Google Authenticator. Ручная реализация сложна и требует генерации и проверки одноразовых кодов.


// Упрощённая идея: после входа отправляем код на email (не настоящий 2FA)
add_action('wp_authenticate', 'send_2fa_code');
function send_2fa_code($username) {
    $user = get_user_by('login', $username);
    if (!$user) return;
    $code = wp_rand(100000, 999999);
    update_user_meta($user->ID, '2fa_code', $code);
    wp_mail($user->user_email, 'Код подтверждения', 'Ваш код: ' . $code);
}

Настоящую двухфакторную аутентификацию лучше доверить специальным плагинам - они реализуют хранение секретов, TOTP и бэкап-коды.

Возможные проблемы:

  • Отправка email может задерживаться - кнопка входа остаётся заблокированной.
  • Плагины могут конфликтовать с кастомными формами входа.
  • Восстановление доступа при потере телефона/почты требует заранее настроенных резервных кодов.

4. Как использовать .htaccess для блокировки доступа к wp-login.php?

Ограничьте доступ по IP или всем, кроме роута. Пример блокировки всех IP, кроме 1.2.3.4:



Order Deny,Allow
Deny from all
Allow from 1.2.3.4
Allow from 5.6.7.8

Для динамического IP - используйте пароль вместо IP. Пример защиты через AuthType:



AuthType Basic
AuthName "Restricted Area"
AuthUserFile /path/to/.htpasswd
Require valid-user

Возможные проблемы:

  • При смене IP (например, у провайдера) доступ будет потерян - используйте микс методов.
  • При включении кэширования страница входа может не проверять пароль повторно - отключите кэширование для wp-login.php.
  • Ошибки в .htaccess могут привести к 500 Internal Server Error - проверяйте синтаксис.

5. Как перенаправить wp-login.php на 404?

Используйте фильтр template_redirect:


// Перенаправление wp-login.php на 404
add_action('template_redirect', 'redirect_wp_login');
function redirect_wp_login() {
    global $pagenow;
    if ($pagenow === 'wp-login.php' && !is_user_logged_in()) {
        status_header(404);
        get_template_part('404');
        exit;
    }
}

Этот код должен быть в functions.php. Если сайт использует кэш, страница 404 может быть закэширована - исключите wp-login.php из кэширования.

Возможные проблемы:

  • Плагины, выполняющие редиректы до template_redirect, могут помешать работе кода.
  • Администраторы, которые знают новый URL входа, не смогут зайти, если не указан новый адрес - нужно параллельно изменить URL (см. основной вариант).
  • 404 страница должна существовать, иначе будет бесконечный цикл.

Расширенные примеры реализации

Пример 1. Полный код для скрытия wp-login.php с редиректом

Этот пример включает изменение URL и отправку старого адреса на 404 через wp_redirect.

Пример

// functions.php
add_filter('site_url', 'custom_login_url', 10, 3);
add_filter('network_site_url', 'custom_login_url', 10, 3);
function custom_login_url($url) {
    if (strpos($url, 'wp-login.php') !== false) {
        $url = str_replace('wp-login.php', 'admin-cabinet', $url);
    }
    return $url;
}

// Перенаправление старого адреса
add_action('init', 'block_wp_login');
function block_wp_login() {
    if (isset($_SERVER['REQUEST_URI']) {
        $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        if ($uri === '/wp-login.php' || $uri === '/wp-login.php/') {
            if (!is_user_logged_in()) {
                status_header(404);
                wp_die('Страница не найдена');
            }
        }
    }
}
Результат: после активации кода стандартная страница /wp-login.php возвращает 404. Новый адрес входа - /admin-cabinet. Все внутренние ссылки на форму входа также переписываются на новый адрес.

Пример 2. Интеграция Google reCAPTCHA с обработкой ошибок

Расширенный код с проверкой сетевых ошибок и запасным пропуском капчи.

Пример

add_action('login_enqueue_scripts', 'add_recaptcha_assets');
function add_recaptcha_assets() {
    wp_enqueue_script('grecaptcha', 'https://www.google.com/recaptcha/api.js', array(), null, true);
}

add_action('login_form', 'recaptcha_field_html');
function recaptcha_field_html() {
    echo '
'; } add_filter('wp_authenticate_user', 'validate_recaptcha', 10, 2); function validate_recaptcha($user, $password) { if (empty($_POST['g-recaptcha-response'])) { return new WP_Error('empty_captcha', 'Пожалуйста, пройдите проверку reCAPTCHA.'); } $response = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', array( 'body' => array( 'secret' => 'ВАШ_SECRET_KEY', 'response' => $_POST['g-recaptcha-response'], 'remoteip' => $_SERVER['REMOTE_ADDR'] ) )); if (is_wp_error($response)) { // Сетевая ошибка - пропускаем капчу (или блокируем?) return $user; // пропуск } $body = json_decode(wp_remote_retrieve_body($response)); if (!$body->success) { return new WP_Error('captcha_fail', 'Не удалось пройти проверку. Попробуйте позже.'); } return $user; }
Результат: при попытке входа без нажатия на капчу выводится ошибка. Если капча пройдена, вход продолжается. При сбое связи с Google ошибка не возникает, пользователь может войти.

Пример 3. Ограничение попыток с учётом прокси-серверов

Код определяет реальный IP через заголовки HTTP_CF_CONNECTING_IP или HTTP_X_FORWARDED_FOR.

Пример

function get_client_ip() {
    $ip = '';
    if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
    } else {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    return $ip;
}

add_filter('authenticate', 'check_attempts_with_ip', 30, 3);
function check_attempts_with_ip($user, $username, $password) {
    $ip = get_client_ip();
    $attempts = get_transient('attempts_' . $ip);
    if ($attempts >= 3) {
        return new WP_Error('blocked', 'Слишком много попыток с вашего IP. Повторите через 5 минут.');
    }
    return $user;
}

add_action('wp_login_failed', 'increase_attempts_ip');
function increase_attempts_ip() {
    $ip = get_client_ip();
    $key = 'attempts_' . $ip;
    $attempts = (int) get_transient($key);
    set_transient($key, $attempts + 1, 300);
}

add_action('wp_login', 'clear_attempts_ip');
function clear_attempts_ip() {
    $ip = get_client_ip();
    delete_transient('attempts_' . $ip);
}
Результат: IP пользователя определяется корректно даже за Cloudflare или прокси. После трёх неудачных попыток вход блокируется на 5 минут. При успешном входе счётчик обнуляется.

Пример 4. .htaccess для доступа к wp-login.php только с белых IP и паролем

Пример


  Order Deny,Allow
  Deny from all
  # Разрешить своим IP
  Allow from 192.168.0.1
  Allow from 10.0.0.0/8
  # Парольная защита для остальных (при необходимости)
  AuthType Basic
  AuthName "Admin Only"
  AuthUserFile /var/www/.htpasswd
  Require valid-user
  Satisfy any

Результат: пользователи с белого списка проходят без пароля. Остальных просят ввести логин и пароль Basic-аутентификации. При этом сам wp-login.php по-прежнему доступен только для тех, кто пройдёт одно из условий.

Пример 5. Перенаправление wp-login.php на 404 с сохранением возможности входа через кастомный URL

Комбинированный подход: скрываем wp-login.php, меняем URL (как в примере 1), а старый адрес отдаём 404.

Пример

// Предположим, новый URL - /admin
add_filter('site_url', 'change_login_url', 10, 3);
function change_login_url($url) {
    if (strpos($url, 'wp-login.php') !== false) {
        $url = str_replace('wp-login.php', 'admin', $url);
    }
    return $url;
}

add_action('init', 'custom_404_for_old_login');
function custom_404_for_old_login() {
    $request_uri = $_SERVER['REQUEST_URI'];
    if (preg_match('/^\/wp-login\.php/', $request_uri)) {
        global $wp_query;
        $wp_query->set_404();
        status_header(404);
        get_template_part('404');
        exit;
    }
}
Результат: при обращении к /wp-login.php выводится тема 404. Реальная страница входа доступна только по новому адресу /admin. Все ссылки на вход в системе также ведут на /admin.

Страница входа WordPress - comments

En
Wp login php 1 (php)