Skip to content

v0.4.2

Choose a tag to compare

@fabiomb fabiomb released this 29 Sep 20:38
· 42 commits to main since this release

Corrección: Las imágenes rotas bloqueaban a quien visitaba la página (#1)

La .htaccess de WordPress manda a PHP todo archivo inexistente, así que una imagen o un CSS faltante también termina en un 404 de WordPress, y el rate limit de 404 (10 por minuto por defecto) los contaba igual que a una sonda de rutas. Una página con varios recursos rotos (típico tras una migración) o los apple-touch-icon*.png que pide iOS bastaban para bloquear al visitante durante 15 minutos.

  • WPS_Loader::should_count_404() (nuevo): los 404 de recursos estáticos (imágenes, CSS, JS, fuentes, source maps) ya no cuentan para el límite. Los de páginas, REST y demás rutas siguen contando.

Seguridad: Los detectores se salteaban agregando una extensión estática a la URL (#2)

Una petición se clasificaba como recurso estático mirando sólo el final de la ruta. /wp-admin/admin-ajax.php/x.css?action=…&id=1 UNION SELECT… ejecuta admin-ajax.php (el resto de la ruta llega como PATH_INFO), pero terminaba en .css: los detectores de SQLi, XSS, path traversal y scanner no la analizaban, y no sumaba al rate limit de páginas. Bastaba ese sufijo para atacar cualquier script PHP del sitio sin que el firewall mirara la petición.

  • WPS_Request::is_static_path() (nuevo): una ruta que atraviesa un .php/ nunca es estática, con cualquier combinación de mayúsculas y también con la barra codificada (%2F). La petición se clasifica según el script que ejecuta (ajax, login, xmlrpc, page) y pasa por todos los detectores.

Seguridad: Los cuerpos JSON no se analizaban (#3)

Los detectores de SQLi, XSS y path traversal sólo recorrían $_POST, que PHP llena únicamente con formularios enviados por POST. Un cuerpo JSON (el formato habitual de la REST API y de muchos plugins) o un formulario enviado con PUT, PATCH o DELETE pasaba sin analizar, y ahí suelen estar las vulnerabilidades de plugins de terceros.

  • WPS_Request::body_values() (nuevo): reúne los valores string del cuerpo, de $_POST, de cuerpos JSON (application/json y variantes como application/merge-patch+json) y de formularios x-www-form-urlencoded enviados con otros métodos. Los tres detectores lo usan en lugar de recorrer $_POST por su cuenta.
  • Límites: se decodifican cuerpos de hasta 1 MB. Un JSON inválido o más grande se analiza como texto (truncado a 1 MB). Se analizan hasta 1000 valores por separado y los sobrantes se juntan en un único texto que también se analiza, así que rellenar con miles de valores basura no alcanza para esconder un payload.
  • A tener en cuenta: las peticiones JSON de visitantes sin permisos de edición (por ejemplo, el checkout por la Store API de WooCommerce) se analizan ahora igual que los formularios. Los usuarios con edit_posts siguen exentos, así que el editor de bloques no se ve afectado.

Seguridad: Fuerza bruta sin límite contra application passwords (#4)

La autenticación Basic de la REST API con application passwords no dispara wp_login_failed sino application_password_failed_authentication, que el detector de login no escuchaba. Se podían probar contraseñas contra /wp-json/ sin que ningún intento se contara ni se bloqueara la IP.

  • WPS_Login_Detector::on_application_password_failed() (nuevo): cada fallo se registra y se evalúa igual que un login fallido, con el mismo máximo de intentos, bloqueo y escalada. El usuario se toma de la cabecera Basic, como hace WordPress.
  • Sólo cuentan los intentos reales (contraseña incorrecta, usuario o email inexistente). Los errores por application passwords desactivadas vienen de clientes mal configurados y no se cuentan.
  • El evento de login fallido indica ahora el método (password o application_password).

Versión

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