Одноразовые числа (nonce)

Во введении упоминалось, что для проверки прав есть два подхода: это «Проверка прав пользователя» и дополняющие эту проверку «Одноразовые числа».

«Одноразовые числа» - это некий токен в WordPress - случайно созданная строка из 10 знаков, например bec698e7ea, которую можно повторить в течении короткого промежутка времени (24 часа). Т.е. создать и проверить это число можно только в течении 24 часа, после этого, та же самая операция вернет уже другое «число».

Такие числа называются в WordPress «Nonce» - «Number used ONCE» (число, использующееся 1 раз) и используются для дополнительной защиты GET и POST запросов. Их можно воспринимайте как секретные приметы, чтобы убедиться, что действие санкционировано.

Подробнее см. описание функции wp_create_nonce()

Пример работы Nonce

Мы зашли в админку, в настройки, при заходе сгенерировалась форма, где одно из полей - это такое «nonce значение». Мы изменили данные и нажали кнопку «сохранить». При обработке данных, кроме проверки прав доступа (current_user_can()) WordPress проверяет сходится ли nonce число. Если nonce не сходится, то запрос на сохранение данных провалится.

Мы не замечаем этих nonce проверок, потому что обычно все происходит быстро. Но если, например, зайти на страницу настроек в админку, подождать 24 часа и только потом попытаться сохранить данные, то внесенные изменения не сохранятся, потому что nonce проверка не будет пройдена.

Зачем нужна проверка «одноразовыми числами»?

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

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

Например, создать страницу и в ней использовать такой код:

<img src="http://example.com/wp-admin/post.php?post=123&action=trash" />

Или например, написать письмо администратору сайта, в которое подсунуть внешне безобидную ссылку...

Авторизованный юзер (админ), кликнет по ссылке, и в результате выполниться JS код хакера, который, к примеру, вышлет данные авторизации. И вот, некогда неавторизованный хакер стал администратором вашего сайта...

Чтобы, невозможно было проделать такую хитрость, существуют «одноразовые числа» (nonce защита).

Так как nonce число устаревает, то если добавить это число в URL или в параметр формы и затем проверять его, URL или форма будут рабочими только 24 часа. А затем, запросы создаваемые URL или формой просто не будут проходить проверку. Таким образом, если хакер заставит вас кликнуть по известной ему ссылке, то ссылка скорее всего уже будет нерабочей, потому что не пройдет проверку «одноразового числа».

Nonce Функции

Для управления одноразовыми числами в WordPress есть специальные функции. Основные из них:

wp_nonce_field( $action, $name )
Создает input поле с одноразовым числом для формы.
wp_create_nonce( $action )
Просто создает одноразовое число в виде строки.
wp_nonce_url( $url, $action )
Добавляет nonce в переданный URL в виде параметра запроса ?_wpnonce=9d6bd884a1
wp_verify_nonce( $nonce, $action )
Проверяет одноразовое число.
check_ajax_referer()
Проверяет Ajax запрос, на соответствие nonce коду. Обрывает работу скрипта через die, если проверка не пройдена.

Пример использования Nonce защиты

Это пример из раздела проверка прав пользователя, только к нему добавляется еще и nonce проверка.

При создании ссылки, используем wp_create_nonce(), чтобы добавить одноразовое число к ссылке:

// функция выводит ссылку с nonce числом на удаление записи
function frontend_delete_link() {
	// проверка прав
	if( ! current_user_can( 'edit_others_posts' ) ) return;

	$url = add_query_arg(
		array(
			'action' => 'frontend_delete',
			'post'   => get_the_ID();
			'nonce'  => wp_create_nonce( 'nonce_frontend_delete' ),
		),
		home_url(),
	);

	printf( '<a href="%s">Удалить</a>', esc_url( $url ) );
}

Аргумент, передаваемый функции гарантирует, что одноразовое число уникально для данного конкретного действия.

Затем, при обработке запроса, проверяем одноразовое число:

if ( isset( $_REQUEST['action'] ) && 'frontend_delete' === $_REQUEST['action'] ) {
	add_action( 'init','frontend_delete_post' );
}

function frontend_delete_post(){
	// проверим право
	if( ! current_user_can( 'edit_others_posts' ) ) return;

	// проверим nonce
	if( ! wp_verify_nonce( $_REQUEST['nonce'], 'nonce_frontend_delete' ) ) return;

	// ID записи
	$post_id = ( isset( $_REQUEST['post'] ) ?  get_post( (int) $_REQUEST['post'] ) : false;

	// нет ID записи
	if( empty( $post_id ) ) return;

	// Удаляем запись
	wp_trash_post( $post_id );

	// редирект в админку
	$redirect = admin_url( 'edit.php' );
	wp_safe_redirect( $redirect );

	exit;
}

Проверка referer

Кроме Nonce есть еще проверка referer. Она дополняет nonce защиту. Это независимая проверка и работает она так: из формы передается параметр, где указан HTTP_REFERER, при обработке формы этот параметр проверяется и если запрос пришел не с ожидаемой страницы, то проверка проваливается.

Для referer проверки в WordPress есть 3 функции:

Заключение

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