Как обезопасить форму авторизации 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.