Уязвим ли wp-cron.php для внешних атак и как его защитить?
Я использую WordPress v.4.1, все плагины и тема обновлены до последних версий.
Я вижу в моих лог-файлах слишком много таких записей...
xxx.xxx.xxx.xxx - - [02/Jan/2015:13:30:27 +0200] "POST /wp-cron.php?doing_wp_cron=1420198227.5184459686279296875000 HTTP/1.0" 200 - "-" "WordPress/217; http://www.example.com"
где xxx.xxx.xxx.xxx - это IP-адрес сервера, на котором размещен сайт, а "http://www.example.com" - это мой веб-сайт.
Существует ли известная уязвимость (эксплойт), затрагивающая wp-cron.php?
Есть ли способ "защитить" этот файл?
Спасибо!

В файле wp-includes/default-filters.php
можно найти регистрацию callback-функции:
// WP Cron
if ( !defined( 'DOING_CRON' ) )
add_action( 'init', 'wp_cron' );
Если теперь перейти к функции wp_cron()
, мы увидим следующее:
$schedules = wp_get_schedules();
foreach ( $crons as $timestamp => $cronhooks ) {
if ( $timestamp > $gmt_time ) break;
foreach ( (array) $cronhooks as $hook => $args ) {
if ( isset($schedules[$hook]['callback']) && !call_user_func( $schedules[$hook]['callback'] ) )
continue;
spawn_cron( $gmt_time );
break 2;
}
}
Функция spawn_cron()
отправляет POST-запрос, который вы видите в своих логах:
$doing_wp_cron = sprintf( '%.22F', $gmt_time );
set_transient( 'doing_cron', $doing_wp_cron );
/**
* Фильтр аргументов запроса cron.
*
* @since 3.5.0
*
* @param array $cron_request_array {
* Массив аргументов URL запроса cron.
*
* @type string $url URL запроса cron.
* @type int $key 22-значное время в микросекундах по GMT.
* @type array $args {
* Массив аргументов запроса cron.
*
* @type int $timeout Таймаут запроса в секундах. По умолчанию .01 секунды.
* @type bool $blocking Блокирующий ли запрос. По умолчанию false.
* @type bool $sslverify Проверять ли SSL для запроса. По умолчанию false.
* }
* }
*/
$cron_request = apply_filters( 'cron_request', array(
'url' => add_query_arg( 'doing_wp_cron', $doing_wp_cron, site_url( 'wp-cron.php' ) ),
'key' => $doing_wp_cron,
'args' => array(
'timeout' => 0.01,
'blocking' => false,
/** Этот фильтр документирован в wp-includes/class-http.php */
'sslverify' => apply_filters( 'https_local_ssl_verify', false )
)
) );
wp_remote_post( $cron_request['url'], $cron_request['args'] );
Здесь также видно, откуда берется число с плавающей точкой: оно передается как аргумент для идентификации транзиента.
Беспокоиться не о чем.

Если вы хотите защитить файл, можно ограничить доступ к нему через ваш httpd.conf (глобальный конфигурационный файл Apache).
# Файл wp-cron.php WordPress
<Files "wp-cron.php">
Require ip 1.2.3.4
</Files>
Замените IP в примере на IP вашего сервера. Это позволит вам по-прежнему получать доступ к файлу с сервера, используя команду, например:
wget -q -O - domain.com/wp-cron.php?doing_wp_cron
И будет возвращаться ошибка 403 (доступ запрещён для запросов с любых других IP). Если вы используете дополнительное правило, как показано ниже, вы можете перенаправлять внешние запросы с 403 Forbidden
на другую страницу (например, на главную), что не является обязательным.
ErrorDocument 403 https://www.domain.ca
Ещё лучше использовать Require ip 127.0.0.1
в примере выше и запрос wget: wget -q -O - 127.0.0.1/wp-cron.php?doing_wp_cron
. Это задействует loopback-интерфейс, и ваш запрос не будет перенаправляться в публичный интернет и обратно.

который будет блокировать вызовы из cron ОС, обычно выполняемые с помощью wget

Можете указать, где в исходном коде вызывается wget? Быстрый поиск не помог мне быстро найти это. Я нашел несколько упоминаний wget, но только в плагинах WordFence и BulletProof.

WordPress не использует cron ОС. Также, используя указанное выше правило, я смог выполнить wget wp-cron.php, используя как wget http://localhost/wp-cron.php, так и wget http://127.0.0.1/wp-cron.php. Однако при попытке доступа извне я вижу следующее в access_log: "GET /wp-cron.php HTTP/1.1" 302 (перенаправление). Потому что у меня также есть ErrorDocument 403 https://www.domain.com/index.php, который перенаправляет все запрещенные запросы на главную страницу.

cron в WordPress вызывается через HTTP-запросы из ядра системы. Все руководства в интернете рекомендуют использовать wget для запуска WP cron из системного cron в качестве замены нативного механизма WordPress. Кроме того, фильтрация по IP-адресу, которая всегда является проигрышной стратегией, в данном случае еще хуже — при переносе сайта cron либо перестанет работать без очевидной причины, либо эти правила просто потеряют всякий смысл.

В таком случае используйте 127.0.0.1 вместо публичного IP-адреса сервера, как я упомянул в своем ответе.

WordPress никогда не использует wget
для WP-Cron, и если вы настраиваете задания cron на уровне ОС, вам, вероятно, также не следует использовать wget
. Этот ответ умный, но недостаточно полный... вы могли бы ограничить доступ к wp-cron.php
, но затем использовать либо WP-CLI, либо /usr/bin/php
для его локального вызова.
