¿Es wp-cron.php vulnerable a ataques externos y cómo protegerlo?
Estoy usando WordPress v.4.1 y todos los plugins y el tema están actualizados.
Veo en mis archivos de registro demasiadas entradas como estas...
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"
donde xxx.xxx.xxx.xxx es la dirección IP del servidor donde está alojado el sitio web y "http://www.example.com" es mi sitio web.
¿Existe alguna vulnerabilidad (exploit) conocida que afecte a wp-cron.php?
¿Hay alguna forma de "proteger" el archivo?
¡Gracias!
En wp-includes/default-filters.php
podemos encontrar el registro de un callback:
// WP Cron
if ( !defined( 'DOING_CRON' ) )
add_action( 'init', 'wp_cron' );
Si ahora vamos a la función wp_cron()
, vemos esto:
$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()
envía la petición POST que estás viendo en tus logs:
$doing_wp_cron = sprintf( '%.22F', $gmt_time );
set_transient( 'doing_cron', $doing_wp_cron );
/**
* Filtra los argumentos de la petición cron.
*
* @since 3.5.0
*
* @param array $cron_request_array {
* Un array de argumentos URL para la petición cron.
*
* @type string $url La URL de la petición cron.
* @type int $key El número GMT de 22 dígitos en microtiempo.
* @type array $args {
* Un array de argumentos para la petición cron.
*
* @type int $timeout El tiempo de espera de la petición en segundos. Por defecto .01 segundos.
* @type bool $blocking Si se debe establecer bloqueo para la petición. Por defecto false.
* @type bool $sslverify Si se debe verificar SSL para la petición. Por defecto 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,
/** Este filtro está documentado en wp-includes/class-http.php */
'sslverify' => apply_filters( 'https_local_ssl_verify', false )
)
) );
wp_remote_post( $cron_request['url'], $cron_request['args'] );
Aquí también puedes ver de dónde viene el número flotante: Se pasa como argumento para identificar el transient.
Nada de qué preocuparse.

Si deseas proteger el archivo, puedes restringir el acceso al mismo a través de tu archivo httpd.conf (archivo de configuración global de Apache).
# Archivo wp-cron.php de Wordpress
<Files "wp-cron.php">
Require ip 1.2.3.4
</Files>
Reemplaza la IP en el ejemplo con la IP de tu servidor. Esto aún te dará acceso al archivo desde el servidor usando un comando como:
wget -q -O - dominio.com/wp-cron.php?doing_wp_cron
Y devolverá un 403 (acceso denegado a solicitudes desde cualquier otra IP). Si usas una regla adicional como la siguiente, redirigirás las solicitudes externas desde 403 Forbidden
a otra página (como la página de inicio), lo cual no es realmente necesario.
ErrorDocument 403 https://www.dominio.ca
Incluso mejor, puedes usar Require ip 127.0.0.1
con el ejemplo anterior y usar la solicitud wget: wget -q -O - 127.0.0.1/wp-cron.php?doing_wp_cron
. Eso utilizará el controlador de red loopback y tu solicitud no se enviará a la internet pública y de vuelta.

que bloqueará las invocaciones desde el cron del sistema operativo que normalmente se realizan usando wget

¿Puedes indicarme dónde se llama a wget en el código fuente? Una búsqueda rápida no me ayudó a encontrarlo rápidamente. Encontré varias referencias a wget, pero solo en los plugins WordFence y BulletProof.

WordPress no usa crons del sistema operativo. Además, usando la regla anterior, pude hacer wget a wp-cron.php usando tanto wget http://localhost/wp-cron.php como wget http://127.0.0.1/wp-cron.php. Sin embargo, al intentar acceder desde el exterior, obtuve lo siguiente en access_log "GET /wp-cron.php HTTP/1.1" 302 (redirección). Esto porque también tengo un ErrorDocument 403 https://www.domain.com/index.php que redirige todos los accesos denegados a la página de inicio.

cron es solicitado mediante HTTP desde el núcleo de WordPress. Todos los tutoriales disponibles en la red sugieren usar wget para activar WP cron desde el cron del sistema operativo como reemplazo al disparador nativo de WP cron. Además, filtrar por IP, que siempre es una estrategia perdedora, es aún peor en este caso porque cuando mueves tu sitio, o el cron dejará de funcionar y no sabrás por qué o esas líneas dejarán de tener efecto.

Usa 127.0.0.1 en lugar de la IP pública del servidor como mencioné en mi respuesta entonces.

WordPress nunca usa wget
para WP-Cron y si decides configurar trabajos cron a nivel de sistema operativo, probablemente tampoco deberías usar wget
. Esta respuesta es inteligente, pero no va lo suficientemente lejos... podrías restringir el acceso a wp-cron.php
pero luego usar WP-CLI o /usr/bin/php
para llamarlo localmente.
