22 abril 2013

Mod_dynip: Cómo proteger wordpress de ataques de fuerza bruta




En las últimas semanas se ha publicado en diferentes medios que ha habido una oleada de ataques de fuerza bruta, o de diccionario, contra el panel de administración de plataformas Wordpress. Por defecto, las herramientas de administración de este tipo de infraestructuras suele estar en la URI /wp-admin.

Es decir, que mediante http://www.ejemplo.com/wp-admin se accedería a una bonita pantalla de autenticación que espera un usuario admin y una contraseña. 



Este es el escenario ideal para este tipo de ataque de fuerza bruta o de diccionario, que prueban de forma sucesiva combinaciones de usuarios/contraseñas hasta dar con algún par que tenga la suficiente debilidad.

Como medidas de seguridad para wordpress ya hemos recomendado, entre otras, la utilización de algún módulo que incluya un CAPTCHA que permita evitar la actividad maliciosa de algunos bots.

Sin embargo, y como soy de la opinión que "muerto el perro, se acaba la rabia", creo que la mejor de las medidas que se pueden tomar para proteger la administración del Wordpress es acotar, mediante las herramientas que provee el propio servidor web,  las direcciones IP desde las que se puede llegar a ver lo que hay tras el directorio /wp-admin. 

En Apache, tanto mediante ficheros .htaccess o en un fichero de configuración, mod_access o en Apache 2.2.X y 2.4.X mod_access_compat, permiten generar, para el "Location /wp-admin" un bloque del tipo

<Location /wp-admin>
Order deny,allow
Allow from 192.168.1.0/24
Deny from All
</Location>

Para muchos de nosotros, que disponemos de una dirección IP fija o que gestionamos el wordpress en un servidor "in-house" el problema se termina ahí. Permites únicamente el direccionamiento interno de forma manual y a correr. Si el servidor se encuentra en algún hosting, quien tiene que administrarlo cuenta con una dirección IP pública fija, y te permiten configurar un bloque como el anterior, también esto se arregla ahí.  

Sin embargo, si nuestro ADSL dispone de una IP dinámica, o si nos encontramos fuera de nuestra red. ¿Cómo lo hacemos? Inicialmente pensé que como mod_access tiene una opción mediante la que podemos indicar un dominio, de manera que todos sus subdominios estarán permitidos o denegados (dependiendo de si usamos una directiva Allow o Deny en Apache), podríamos crear un dominio del tipo dyndns o no-ip.info, que podamos actualizar según la IP que tengamos y hacer que Apache nos abra sus puertas.

Después de hacer un montón de pruebas, y ayudado de tcpdump, se ve que el funcionamiento de Apache, cuando llega una petición a /wp-admin, hace una resolución DNS inversa de la IP origen. De esta manera, si ve que coincide con lo definido en la directiva con el subdominio, le permite la entrada. Pero esto no funciona para lo que queremos, puesto que el dominio devuelto en la resolución inversa no coincide con el dominio Dyndns, sino que suele referirse al nombre de la red del proveedor de Internet. 

Así que, tras buscar opciones y otros módulos existentes para lo que yo quería, decidí hacérmelo yo mismo.
Para no volverme loco con la creación de directivas nuevas, he sustituido los típicos Order, Allow y Deny por "Orden", "Permitir" y "Denegar" (typical spanish :D). De esta forma el bloque que necesitaríamos para proteger /wp-admin sería algo así:

<Location /wp-admin>
Orden denegar,permitir
Permitir from anywhere.dyndns.com
Denegar from All
</Location>

Para permitir la IP donde estemos, sólo tenemos que habilitar, o desde la propia página web de dyndns/no-ip, la API del proveedor, aplicaciones para dispositivos móviles, etc,… Una vez gestionado el dominio, una buena práctica sería modificar el valor IP a 127.0.0.1, para que quede la puerta cerrada al exterior.

Como ya sabéis, para compilar e incluir el módulo en vuestro Apache debéis ejecutar:

apxs -i -a  -c  mod_dynip.c -n mod_dynip

Con este comando el fichero mod_dynip.so se creará y colocará donde corresponda, y se editará el fichero httpd.conf, e incluirá en la sección correspondiente la línea "LoadModule dynip_module       /usr/lib64/httpd/modules/mod_dynip.so"

Bueno venga… ¿y las tripas del módulo, qué hacen?

Fundamentalmente, lo que modifiqué del código de mod_access, es el comportamiento del mismo cuando se encuentra un dominio en una directiva Allow o Domain. Así, lo que hace primero es resolver, dado ese dominio, todas las direcciones IP asociadas. En el caso de un dominio dinámico, será sólo uno, pero si lo queremos usar para un dominio estático, que tenga varias IPs, funcionará igualmente. Por cada una de las direcciones IP encontradas, las irá compararando contra la dirección IP origen detectada por el servidor web y si coincide la dejará pasar o no (dependiendo si es un Permitir o Denegar). 

Aunque faltan varios includes y cambios menores, que podéis ver en la descarga, la parte más importante del código es la siguiente: 


Después de una semana puesto en mis servidores en producción (soy así de osado), puedo decir que funciona estable y correctamente con direccionamiento IPv4.  

6 comments :

Paco Castillo dijo...

Pues que alguien le diga a los chicos de WP que la URL de acceso a la herramienta debería ser personalizable ($URL_ADMIN) y no estár "escrita a fuego" en la aplicación... pero eso es solo mi opinión. :-)


En cualquier caso, gracias por la aclaración.

Javi dijo...

Hola, me he decidido a probar el modulo y el caso es que me resuelve correctamente las IP's de dominios dyndns, pero siempre me deniega el acceso, incluso con IP fija en la configuración de apache.

Lorenzo_Martinez dijo...

Si te parece, envíame un correo con la configuración del Location que has definido y te echo una mano a ver si encontramos el fallo...

Javi dijo...

Muchas gracias Lorenzo, por ofrecerte a echarme una mano. Ya te he enviado un mail.

Javi dijo...

He conseguido solucionar mi problema y de paso he encontrado un pequeño bug en el código del modulo ;-).


Lorenzo, te he enviado un mail con los detalles.
Mil gracias de nuevo por prestarme tu ayuda.

Jose antonio dijo...

mod_security muy versatil y permite implementar reglas dinamicas por ip