Skip to content

Releases: fabiomb/wp-secure

v0.4.1

Choose a tag to compare

@fabiomb fabiomb released this 22 Sep 22:22

Corrección: El aviso de login desde IP nueva nunca se enviaba

El ajuste Notificar login desde IP nueva venía activado por defecto y se mostraba en Configuración → Notificaciones, pero ningún código invocaba el aviso: un administrador que iniciaba sesión desde una red desconocida nunca generaba el mail.

  • WPS_Admin_Notifier::track_login() (nuevo): se llama desde WPS_Login_Detector::on_login_success() y avisa cuando un usuario con manage_options inicia sesión desde una red que no usó antes. No aplica a clientes, alumnos ni otros usuarios sin permisos de administración, para no inundar el correo en sitios con muchas cuentas.
  • Red conocida: se identifica con la clave de cliente, así que en IPv6 rotar de dirección dentro del mismo prefijo (por defecto /64) no genera avisos. Las redes conocidas se guardan en user meta (wps_known_login_keys, hasta 20 por usuario, se descartan las más viejas) y no en la tabla de intentos de login, que se purga a los pocos días.
  • Sin avisos al instalar: el primer login de un usuario sin historial no avisa, porque toda red sería «nueva». Las redes se registran aunque el aviso esté desactivado, para que activarlo después no dispare un mail por cada red ya usada.
  • WPS_Admin_Notifier::notify_new_login_ip() devuelve ahora si el mail se envió.

Corrección: Los avisos de cambios de configuración y de bloqueos automáticos tampoco se enviaban

Igual que el anterior, notify_settings_change() y notify_auto_block() existían con su ajuste en el panel, pero nada las invocaba. De las cuatro notificaciones configurables sólo funcionaba el resumen diario.

  • Cambios de configuración (activado por defecto): se avisa al guardar la configuración, al activar o desactivar el Modo Inseguro y al importar una configuración desde archivo. El mail indica qué cambió, quién y desde qué IP: apagar el firewall es lo primero que haría quien tomara una cuenta de administrador.
  • Bloqueos automáticos (desactivado por defecto): un mail por bloqueo inundaría el correo durante un ataque, así que ahora se envía un resumen por hora, y sólo si hubo bloqueos. WPS_Blocker encola cada bloqueo automático, de IP o de red, en la opción wps_block_digest (los manuales no, porque los hace el propio administrador). WPS_Admin_Notifier::send_block_digest() (nuevo, reemplaza a notify_auto_block()) corre con el cron horario, envía un único mail con hasta 100 bloqueos detallados más el total, y vacía la cola. La cola vive en WPS_Blocker porque la Capa 1 bloquea antes de que el autoloader del plugin esté registrado.
  • Importar configuración regenera el archivo de la Capa 0, que la importación puede encender o apagar.
  • Desinstalación: elimina también la opción wps_block_digest.
  • configuration.md: la sección de notificaciones describía ajustes que no existen («Frecuencia de resumen» semanal); ahora lista los reales.

Corrección: Consulta extra por petición en sitios actualizados

  • WPS_Blocker::client_key() leía el ajuste ipv6_block_prefix también para IPs IPv4. En instalaciones actualizadas desde versiones anteriores ese ajuste no estaba guardado, y cada petición hacía una consulta a la base de datos para buscarlo. Ahora sólo se lee para IPv6.
  • Migraciones: cada cambio de versión completa los ajustes por defecto que falten (WPS_Activator::set_defaults(), ahora público), así los ajustes nuevos existen también en sitios actualizados.

Versión

  • Versión actualizada a 0.4.1 en la cabecera del plugin, la constante WPS_VERSION y el MU-plugin.

v0.4.0

Choose a tag to compare

@fabiomb fabiomb released this 22 Sep 22:12

Seguridad: En IPv6 bastaba con cambiar de dirección para esquivar el firewall

Un proveedor asigna normalmente un /64 entero a cada cliente IPv6: 18 trillones de direcciones que el cliente puede usar a voluntad, una distinta en cada petición. El rate limiting, el conteo de intentos de login y los bloqueos automáticos trabajaban con la dirección exacta, así que un atacante con IPv6 rotaba de dirección y ninguno de los tres lo alcanzaba: nunca superaba un límite, nunca acumulaba intentos fallidos y cada bloqueo caía sobre una dirección que ya no usaba.

  • Clave de cliente (WPS_Ip_Utils::client_key(), nuevo): en IPv4 es la IP; en IPv6, la red del prefijo configurado.
  • Nuevo ajuste ipv6_block_prefix (Configuración → Firewall Avanzado → Prefijo IPv6 por cliente): por defecto 64, admite de 48 a 128. 128 vuelve al comportamiento anterior (dirección exacta). Un valor fuera de rango vuelve al valor por defecto, para que un campo vacío no termine bloqueando un /48.
  • Rate limiting: WPS_Rate_Limiter cuenta por clave de cliente.
  • Intentos de login: WPS_Login_Detector registra y cuenta los intentos por clave de cliente, así que el máximo de intentos fallidos y el umbral de usuarios inexistentes valen para toda la red.
  • Bloqueos automáticos (WPS_Blocker::block_offender(), nuevo): los detectores de SQLi, XSS, path traversal, scanner y crawlers falsificados, el rate limiter, el detector de login y el motor de riesgo bloquean en IPv6 la red completa como rango CIDR. La Capa 0 ya aplicaba rangos, con su vencimiento. Mantiene las salvaguardas de block_ip() (nunca el propio servidor ni una IP de la whitelist) y, si la red incluye la IP del servidor, bloquea sólo la dirección exacta: la Capa 0 no exime al servidor y le cortaría wp-cron.
  • Escalada de bloqueos: WPS_Blocker::count_previous_blocks() cuenta los bloqueos de la dirección y los de su red, así que la escalada a bloqueos largos y permanentes sigue funcionando con bloqueos de red.
  • Sin cambios: los bloqueos manuales, las reglas personalizadas y la whitelist siguen usando la dirección exacta. El log de eventos y el de tráfico siguen registrando la dirección exacta; el evento de bloqueo agrega la red bloqueada en sus detalles.

Algunos proveedores de hosting comparten un /64 entre servidores de clientes distintos. Si aparecen bloqueos de red que alcanzan a terceros legítimos, se puede subir el prefijo o usar la whitelist.

Documentación

  • configuration.md: la tabla de rate limiting describía ajustes que no existen («Activar rate limiting», «Ventana de análisis»); ahora lista los reales. Se documenta el prefijo IPv6 y el resultado no verificado de la verificación de crawlers.

Versión

  • Versión actualizada a 0.4.0 en la cabecera del plugin, la constante WPS_VERSION y el MU-plugin.

v0.3.1

Choose a tag to compare

@fabiomb fabiomb released this 22 Sep 21:49

Continuación de la revisión de la 0.3.0: Capa 0, resiliencia ante fallas externas y documentación.

Corrección: La Capa 0 aplicaba bloqueos vencidos y no se enteraba de los desbloqueos

La Capa 0 corre antes de WordPress y no consulta la base de datos: sólo conoce lo que dice su archivo de datos, y ese archivo se regeneraba únicamente con el cron horario.

  • Desbloquear una IP desde el panel no tenía efecto en la Capa 0 hasta la siguiente pasada del cron. Lo mismo al agregar una IP a la whitelist, incluso la propia.
  • Los bloqueos temporales no vencían en la Capa 0: el archivo no guardaba el vencimiento, así que un bloqueo de 15 minutos seguía vigente hasta una hora más, o indefinidamente si el cron no corría.
  • La whitelist por rango CIDR se ignoraba en la Capa 0.
  • El archivo se escribía en el mismo lugar desde donde se lee. Una petición que lo incluía a mitad de la escritura recibía un ParseError, que el operador @ no suprime, y fallaba con error fatal.

Cambios:

  • Formato del archivo: cada IP y cada CIDR bloqueado lleva su vencimiento como timestamp (0 = permanente), y se agrega whitelist_cidrs. WPS_Firewall_Prepend descarta los bloqueos vencidos por su cuenta y sigue leyendo el formato anterior (valor 1, lista plana de CIDRs) como permanente, así que no hay corte durante la actualización.
  • WPS_Blocker::schedule_layer0_sync() (nuevo): bloquear, desbloquear, hacer permanente un bloqueo o modificar la whitelist regenera el archivo al final de la petición, una sola vez aunque haya varios cambios. También funciona cuando el bloqueo ocurre en la Capa 1 y termina con exit.
  • Guardar la configuración regenera el archivo, o lo elimina si se apagó la Capa 0.
  • WPS_Activator::build_blocked_ips_data() (nuevo): arma el contenido del archivo, separado de la escritura para poder testearlo. Si una IP tiene varios bloqueos activos, gana el más largo.
  • Escritura atómica: el archivo se escribe en un temporal y se renombra, y después se invalida OPcache. Con opcache.validate_timestamps desactivado, antes se seguía sirviendo la versión vieja.
  • Whitelist de la Capa 0: sólo toma las entradas de tipo global. Las de tipo login eximen del detector de login, no de un bloqueo de IP.

Corrección: Una actualización del plugin podía dejar todo el sitio en error fatal

La documentación indicaba apuntar auto_prepend_file a plugins/wp-secure/includes/firewall/wps-firewall-prepend.php. Durante una actualización WordPress borra y vuelve a copiar la carpeta del plugin; mientras el archivo no está, PHP no puede cargar el prepend y cada petición del sitio termina en error fatal, incluido wp-admin. Lo mismo al eliminar el plugin o al renombrar su carpeta, que era justamente el procedimiento de recuperación que proponía la guía de solución de problemas.

  • Cargador estable de la Capa 0 (nuevo): el plugin genera wp-content/wps-data/wps-firewall-loader.php, fuera de su carpeta, y lo mantiene al activarse, actualizarse o cambiar de versión. El cargador incluye el firewall sólo si existe; si el plugin no está, no hace nada. Corre dentro de una función anónima para no dejar variables en el ámbito global de cada petición.
  • WPS_Activator::install_prepend_loader(), build_prepend_loader(), prepend_loader_path() y prepend_points_into_plugin() (nuevos).
  • Aviso en el panel: si auto_prepend_file apunta todavía al archivo dentro del plugin, se muestra una advertencia con la ruta del cargador. La descripción del ajuste de Capa 0 muestra la ruta exacta a configurar.
  • Desinstalación: el cargador se conserva a propósito, porque borrarlo con la directiva todavía activa tumbaría el sitio.

Nuevo: Suspender el bloqueo desde wp-config.php

Hasta ahora, recuperar el acceso tras bloquearse a uno mismo requería borrar el MU-plugin y renombrar la carpeta del plugin. Además de ser riesgoso con la Capa 0 configurada, no alcanzaba: al reactivar, el bloqueo seguía en la base de datos.

  • Constante WPS_DISABLE_BLOCKING: definida en wp-config.php, el firewall sigue detectando y registrando pero no bloquea, igual que el Modo Inseguro. El panel muestra un aviso mientras esté activa.
  • WPS_Blocker::blocking_disabled() (nuevo): reúne el Modo Inseguro y la constante.
  • Login con el bloqueo suspendido: WPS_Login_Detector::check_before_auth() rechazaba el login de una IP bloqueada aunque el Modo Inseguro estuviera activo, porque no pasaba por send_block_response(). Ahora respeta ambos mecanismos, así que el administrador bloqueado puede entrar a desbloquearse.

Corrección: Con la API de geolocalización caída, cada visita esperaba 5 segundos

En modo API, la primera petición de cada IP consulta ipinfo.io dentro de la propia petición del visitante, con un timeout de 5 segundos. Sólo se cacheaban las respuestas exitosas. Si el servicio estaba caído, lento o con la cuota agotada (429), cada IP nueva esperaba el timeout completo, y cada petición siguiente de esa misma IP volvía a intentarlo.

  • Pausa ante fallas del servicio: un timeout, un error de conexión, un 429 o un 5xx suspenden las consultas a la API durante 10 minutos (WPS_Ipdb_Manager::API_BACKOFF_TTL). Mientras tanto se usa la base local si existe, o la petición sigue sin datos de país.
  • Cache de fallos por IP: una IP que no pudo resolverse (respuesta inválida o error propio de esa IP) no se vuelve a consultar durante 15 minutos.
  • Timeout reducido de 5 a 2 segundos.

Seguridad: Una regla personalizada podía permitir que cualquiera se agregara a la whitelist

La acción Agregar a whitelist aceptaba cualquier condición, y la propia documentación proponía como ejemplo User-Agent contiene "UptimeRobot". Cualquiera que enviara ese User-Agent quedaba en la whitelist global de forma permanente, excluido de todo el firewall. Lo mismo con condiciones sobre headers, la URI, el país (alcanzable con una VPN) o con operadores negativos («IP distinta de X» coincide con el resto de Internet).

  • WPS_Custom_Rules::whitelist_conditions_allowed() (nuevo): la acción whitelist sólo admite condiciones sobre el campo ip con equals o cidr. Al guardar una regla que no cumple, el panel explica el motivo.
  • Reglas existentes: las guardadas con versiones anteriores que no cumplen siguen registrando el evento, pero ya no agregan la IP a la whitelist. Las IPs que ya se hubieran agregado por esa vía siguen en la whitelist y conviene revisarlas: se reconocen por la etiqueta «Auto: regla …».
  • Documentación: el ejemplo del servicio de monitoreo usa ahora el rango de IPs publicado por el servicio, y explica cuándo conviene la acción Eximir.

Corrección: Acceder a XML-RPC bloqueaba la IP en todo el sitio

Con XML-RPC desactivado (el valor por defecto), cualquier petición a xmlrpc.php bloqueaba la IP durante 15 minutos para todo el sitio. Eso dejaba fuera a los servidores de Jetpack, a la app móvil de WordPress y a cualquiera que compartiera IP con ellos. Además, la detección buscaba xmlrpc.php en toda la URI, así que bastaba con buscar «xmlrpc.php» en el buscador del sitio.

  • WPS_Xmlrpc_Detector: la petición se sigue rechazando con 403, pero ya no se bloquea la IP. Con XML-RPC desactivado el intento no tiene efecto; el abuso sostenido lo cubre el límite rate_xmlrpc_per_hour, que ahora sí recibe los hits de XML-RPC.
  • WPS_Xmlrpc_Detector::is_xmlrpc_path() (nuevo): sólo cuenta la ruta pedida, no el query string. También se reconoce la constante XMLRPC_REQUEST que define WordPress.
  • Documentación: la «excepción para Jetpack» que describía configuration.md no existía. Se explica cómo lograrlo con una regla Eximir por IP o rango.

Corrección: Security headers duplicados y filtro XSS heredado

  • WPS_Security_Hardener::headers_to_send() (nuevo): no se envía un header que otro plugin, el tema o el servidor ya definieron. Un X-Frame-Options duplicado con valores distintos hace que el navegador lo descarte, y un sitio configurado para embeberse en un dominio propio quedaba roto.
  • X-XSS-Protection pasa de 1; mode=block a 0. El filtro fue retirado de los navegadores y, en los que lo conservan, permite ataques de filtrado selectivo. La recomendación actual es desactivarlo.

Documentación

  • Recuperación de acceso (troubleshooting.md, faq.md, cdn-proxy-setup.md): se reemplaza «borrar el MU-plugin y renombrar la carpeta» por la constante WPS_DISABLE_BLOCKING o, con WP-CLI, el Modo Inseguro. Se advierte no renombrar la carpeta con la Capa 0 apuntando dentro de ella.
  • Nombre del MU-plugin: la documentación decía wps-firewall.php y wps-muplugin.php; los archivos reales son wps-firewall-muplugin.php en ambos lados.
  • Instalación de la Capa 0: apunta al cargador y distingue Apache con mod_php (.htaccess) de PHP-FPM (.user.ini).
  • Desinstalación: describía que se eliminaban las directivas de auto_prepend_file, cosa que el plugin no hace ni puede hacer con seguridad. Ahora explica qué se borra y qué no.
  • Ajustes inexistentes: se eliminan las menciones a un «Nivel de protección» Bajo/Medio/Alto y a un «Modo de operación», que no existen. El asistente se describe con sus cuatro pasos reales.

Corrección: La desinstalación dejaba datos atrás

uninstall.php borraba la ubicación de datos anterior (dentro de la carpeta del plugin) en lugar de wp-content/wps-data/, y no eliminaba la opción wps_unsafe_mode ni los transients de geolocalización y verificación de crawlers (uno por IP, potencialmente miles de filas en wp_options).

  • Se eliminan wp-content/wps-data/ (salvo el cargador de la Capa 0), wps_unsafe_mode, los transients wps_* y el MU-plugin, por si el plugin se eliminó sin desactivarse.

Versión

  • Versión actualizada a 0.3.1 en la cabecera del plugin, la constante WPS_VERSION y el MU-plugin.

v0.3.0

Choose a tag to compare

@fabiomb fabiomb released this 22 Sep 19:46

Versión centrada en eliminar bloqueos a visitantes legítimos y en una vulnerabilidad XSS del panel. Varios cambios afectan cuánto duran los bloqueos y cuándo se aplican, así que después de actualizar conviene revisar WP Seguro → Bloqueos y Eventos durante unos días.

Seguridad: XSS almacenado en Tráfico en Vivo

Un visitante anónimo podía ejecutar JavaScript en la sesión de un administrador. La URI de cada petición se guarda decodificada en el log de tráfico, y la vista en vivo la insertaba dentro de un atributo title="…". La función de escape del panel convertía <, > y &, pero no las comillas, así que una petición a una URI con %22 onmouseover=… cerraba el atributo e inyectaba un manejador de eventos. Ningún detector lo frenaba: el patrón de manejadores on*= exige una etiqueta < previa.

  • WPS.esc() (assets/js/wps-admin.js): escapa ahora también " y ', de modo que es seguro tanto en texto como dentro de atributos. Ya no depende de innerHTML, y convierte a texto valores no string (antes un http_status de 0 se mostraba vacío).
  • Tráfico en Vivo: el enlace a la ficha de la IP también pasa por esc() al insertarse en href.

Corrección: La duración de los bloqueos dependía de la zona horaria de MySQL

Todas las fechas del plugin se guardan en UTC, pero se comparaban contra NOW(), que devuelve la hora en la zona horaria de la sesión MySQL. WordPress no fija esa zona, así que en la práctica rige la del servidor de base de datos.

  • Con MySQL en UTC-3 (lo habitual en hostings argentinos), un bloqueo de 15 minutos duraba 3 h 15 min, la ventana de «intentos de login en la última hora» abarcaba 4 horas y la escalada a bloqueo permanente se alcanzaba mucho antes de lo configurado.
  • Con MySQL en UTC+1 o UTC+2, los bloqueos temporales vencían en el momento de crearse y el conteo de intentos fallidos de login daba siempre cero: la protección contra fuerza bruta quedaba anulada.
  • Todas las consultas (WPS_Blocker, WPS_Login_Detector, WPS_Db_Maintenance, WPS_Activator::sync_blocked_ips_file(), dashboard, notificador, eventos, tráfico y patrones) usan ahora UTC_TIMESTAMP(). Los bloqueos existentes no necesitan migración: ya estaban guardados en UTC y a partir de esta versión se interpretan bien.
  • Nuevo test que recorre includes/ y falla si alguna consulta vuelve a usar NOW(), CURDATE(), CURTIME() o SYSDATE().

Corrección: El rate limit contaba cada visita dos veces y podía bloquear al propio servidor

El MU-plugin (Capa 1) registraba los hits total y pages en muplugins_loaded, y el loader (Capa 2) volvía a registrarlos en init. Cada visita anónima sumaba dos, así que el límite de 60 páginas por minuto era en la práctica de 30. Detrás de un NAT de oficina o del CGNAT de una operadora móvil, donde muchos usuarios comparten IP, eso alcanzaba para bloquearlos a todos.

Además, nada eximía al propio servidor. wp-cron, los loopbacks de Site Health y los precargadores de caché (WP Rocket, LiteSpeed Cache) salen de la IP del servidor, y un precargador supera el límite en segundos. Con la IP del servidor bloqueada, el sitio se queda sin tareas programadas.

  • WPS_Rate_Limiter::record_hit(): cuenta cada tipo una sola vez por petición, aunque lo invoquen ambas capas. Las dos siguen registrando, para cubrir a visitantes anónimos (Capa 1) y a usuarios logueados sin manage_options (Capa 2).
  • WPS_Ip_Utils::is_server_ip() (nuevo): reconoce loopback (127.0.0.0/8, ::1) y la IP con la que el servidor atiende la petición (SERVER_ADDR, o LOCAL_ADDR en IIS).
  • El propio servidor ya no se limita ni se bloquea automáticamente: record_hit() lo ignora, WPS_Blocker::block_ip() rechaza bloquearlo salvo que el bloqueo sea manual, y tanto el MU-plugin como WPS_Loader::check_current_ip() lo dejan pasar aunque una versión anterior lo haya dejado en la lista de bloqueos.
  • MU-plugin: no aplica rate limit a las peticiones de wp-cron (DOING_CRON), igual que ya hacía la Capa 2.
  • WPS_Ip_Utils::ip_in_cidr(): una IP y un CIDR de familias distintas (IPv4/IPv6) ya no coinciden nunca. Antes, una IPv6 contra un rango IPv4 se comparaba con una máscara sin el prefijo de 96 bits y podía coincidir por los ceros iniciales (::1 coincidía con 127.0.0.0/8).

Si el sitio está detrás de un CDN, los loopbacks pueden llegar con la IP pública del servidor en CF-Connecting-IP en lugar de SERVER_ADDR. En ese caso conviene agregar esa IP a la whitelist.

Corrección: Visitantes bloqueados por abrir un post, buscar o comentar

Los detectores bloquean la IP ante la primera coincidencia, y varios patrones coincidían con tráfico completamente normal. Lo más grave estaba en las rutas: una URL de post la repite cada visitante que llega a ella, así que un solo slug desafortunado bloqueaba a todo el tráfico de esa página.

  • WPS_Scanner_Detector::$scanner_paths: los nombres de directorio (phpmyadmin, pma, mysql, myadmin, administrator, manager) exigen ahora un segmento completo de la ruta. \b también corta en un guion, así que /2024/05/mysql-vs-postgresql/ o /blog/manager-de-contenidos/ se tomaban por sondas. admin.php sólo cuenta fuera de /wp-admin/: antes, un usuario con la sesión vencida que volvía a /wp-admin/admin.php quedaba bloqueado.
  • WPS_Scanner_Detector::match_scanner_path() (nuevo): las rutas de scanner se evalúan sobre el path, sin el query string.
  • WPS_Path_Traversal_Detector: los archivos sensibles del sitio (wp-config.php, .htaccess, .env, composer.json, debug.log, volcados .sql, etc.) sólo cuentan cuando son la ruta pedida. En el query string y en los formularios son menciones legítimas: buscar «cómo editar el .htaccess» o comentar «mi wp-config.php no carga» bastaba para quedar bloqueado. En cualquier campo se siguen detectando el recorrido de directorios (../../, en todas sus codificaciones) y los archivos del sistema (/etc/passwd, /proc/self/environ).
  • WPS_Path_Traversal_Detector: el patrón de copias de seguridad (backup|dump|database … .zip) cruzaba todo el valor con .*; ahora exige que sea el nombre del archivo pedido. Los volcados .sql también.
  • WPS_Path_Traversal_Detector::detect_in_path() y detect_in_value() (nuevos): exponen el análisis de cada contexto, lo que permite cubrirlo con tests.
  • WPS_Sqli_Detector::$patterns: eliminado ' OR ' suelto, que coincidía con «¿elijo 'sí' or 'no'?»; la forma de ataque real (' OR '1'='1) la sigue cubriendo el patrón de tautologías. El patrón de comentarios SQL ya no considera el guion doble, habitual en prosa («I tried it -- and it worked»); se mantienen /**/ y /*!…*/, que sí se usan para partir palabras clave.
  • WPS_Xss_Detector::$patterns: expression( sólo cuenta como valor de una propiedad CSS (: expression(). Antes coincidía con «una regular expression (regex)».
  • Nuevos tests de rutas legítimas y sondas reales para scanner y path traversal, y casos nuevos en los de SQLi y XSS.

Corrección: Googlebot real se bloqueaba como crawler falsificado

La verificación de crawlers (PTR + resolución directa) tenía dos fallos que terminaban bloqueando a buscadores reales, con impacto directo en el posicionamiento.

  • IPv6: la resolución directa usaba gethostbyname(), que sólo devuelve IPv4. Googlebot rastrea por IPv6 cuando el sitio tiene registro AAAA (o está detrás de Cloudflare), y en ese caso la IP resuelta nunca coincidía con la del visitante.
  • Fallas de DNS: gethostbyaddr() no distingue «esta IP no tiene PTR» de «el DNS no respondió». Un timeout se trataba como falsificación, se bloqueaba la IP y el veredicto quedaba en cache 24 horas.

Cambios:

  • WPS_Crawler_Verifier::verify_rdns(): usa dns_get_record(), que devuelve una lista vacía ante NXDOMAIN y false ante un error del servidor. La resolución directa consulta A y AAAA, y las IPs se comparan en binario (inet_pton), así que la notación abreviada o expandida de IPv6 da igual. Se revisan todos los nombres PTR, no sólo el primero.
  • WPS_Crawler_Verifier::RESULT_UNVERIFIED (nuevo): resultado para cuando el DNS no respondió o no hay datos de ASN. No aplica el bloqueo por spoofing (la petición sigue el análisis normal) y se cachea 10 minutos en lugar de 24 horas.
  • WPS_Crawler_Verifier::verify_asn(): sin base local ni API de geolocalización devuelve unverified en lugar de spoofed. Antes, el rastreador de Facebook que llegaba por IPv6 se bloqueaba en cualquier sitio sin datos de ASN.
  • Crawler de Facebook: se agrega .fbsv.net a sus dominios de rDNS, que es el que usan sus IPs IPv6.
  • Cache: la clave incluye el crawler además de la IP, para que el veredicto de un UA no se reutilice con otro.
  • WPS_Crawler_Verifier::set_resolver() y arpa_name() (nuevos): permiten testear la verificación con un resolver falso. Los tests ya no dependen de la red.

Corrección: Cada intento de login con usuario inexistente contaba doble

Con un usuario inexistente, check_before_auth() grababa el intento antes de autenticar, para poder contarlo y bloquear en el acto. Después WordPress disparaba wp_login_failed por ese mismo intento y on_login_failed() lo grababa otra vez. Con el umbral por defecto de 3, bastaban dos errores de tipeo en el usuario o el email para bloquear la IP, y el máximo de 5 intentos fallidos se alcanzaba en el tercero.

  • WPS_Login_Detector: si check_before_auth() ya grabó el intento, on_login_failed() no lo vuelve a grabar. Se sigue registrando el evento y evaluando el bloqueo.
  • Tests: el doble de $wpdb registra ahora los inserts, y se agregan stubs de get_user_by(), wp_json_encode() y current_time(). Esto permite verificar el flujo completo authenticatewp_login_failed.

Versión

  • Versión actualizada a 0.3.0 en la cabecera del plugin, la constante WPS_VERSION y el MU-plugin. Al cambiar la cabe...
Read more

v0.2.11

Choose a tag to compare

@fabiomb fabiomb released this 22 Sep 19:21

Versión 0.2.11