En los últimos tiempos es cada vez más frecuente que los sitios web construidos con software libre sean atacados. WordPress es el gestor de contenidos (CMS) más abundante en la red y es con diferencia el que presenta mayor tasa de instalaciones comprometidas. Muchos de los propietarios y usuarios de esos sites ni siquiera saben que han sido atacados. No es infrecuente ver posts llenos de comentarios de spam y otro tipo de problemas. La seguridad, como la calidad, suele perderse por el punto más débil. Si lees con atención este artículo tal vez descubras la debilidad de tu site y puedas solucionarlo. En él os dejo algunos consejos útiles para reducir al mínimo los agujeros de seguridad en sitios webs y blogs basados en WordPress.
Cualquier sitio que pongáis en producción está expuesto desde el principio, la implementación de las medidas de seguridad tiene que ser prácticamente inmediata. Muchas de estas medidas no llevan demasiado tiempo, aunque en su conjunto requiere una mínima dedicación.
Asegura tus herramientas:
- Antes de crear tu web comprueba que tu ordenador está libre de virus y malware y utiliza algún tipo de firewall. Mantén el sistema operativo de tu ordenador actualizado. Cifra el almacenamiento; si trabajas con Windows 8.1 te será muy fácil, pues ya lo hace por defecto; si no, trata de resolverlo. Esto del mantenimiento de tu propia máquina es aburrido, pero mal hecho puede ser una fuente de problemas.
- Utiliza Chrome o en general el navegador más seguro que encuentres en Browserscope. Sigue pautas para asegurar tu navegador y revísalas regularmente contrastando con las prácticas recomendadas por artículos solventes (por ejemplo, éste)
Infraestructura
- Cuando elijas el proveedor de hosting preocúpate por sus medidas de seguridad contra ataques. Si es un entorno LAMP ¿utiliza mod_security, Cloudfare Security? ¿informa sobre su infraesstructura de Firewalls? ¿está especializado en hosting de WordPress?. No te fíes demasiado de los consejos genéricos sobre witios web o sobre precios, investiga un poco, muchos sitios de recomendaciones son afiliados, artículos más o menos interesados para conseguir ingresos. Te sorprenderá ver que hay algunos muy buenos y no tan caros. Además si buscas algún descuento vía afiliados una sencilla búsqueda en Google te ayudará. Los proveedores de hosting te proporcionan normalmente paquetes con versiones determinadas, generalmente recientes de Apache, PHP y MySQL, si tienes control de ello trata de subir a los paquetes de versiones más recientes o si no pídelo periódicamente a tu proveedor.
Acceso remoto
- Utiliza sFTP para subir tus ficheros no FTP estándar, ten en cuenta que algunos de ellos como wp-config.php contienen claves que no deberían ser coocidas. Alternativamente también puede valer SSL o VPNs.
Backups
- Aprende a usar el sistema de backup que provea tu hosting, alternativamente utiliza plugins o herramientas que te puedan ayudar a realizarlo. Dependiendo del volumen de actividad que tenga tu blog deberá ser semanal, diario o incluso cada pocas horas. Recuerda que la aplicación guarda su información clave tanto en un conjunto de ficheros como en una base de datos. Automatiza esta tarea, tú no eres un robot (creo).
Cuentas de administrador y de editor
- Contraseñas: ni tu base de datos ni tu contraseña del blog deberían ser deducibles, ni cortas, ni ser iguales a ninguna otra contrasseña que manejes. Actualmente una contraseña segura se considera aquella que tiene 15 o 16 caracteres, no sólo números y letras sino también símbolos. Recomiendo un generador de contraseñas online como éste: https://strongpasswordgenerator.com/ usando todas las opciones que recomiendan. El método más habitual de ataque es el conocido como Fuerza Bruta. Podrán con cualquier fecha de cumpleaños, nombres de mascota y palabras más o menos ingeniosas. Puedes comprobar el tiempo que se tarda en romper una contraseña en webs como éstas: https://howsecureismypassword.net/ Una de 16 caracteres hecha con el generador da 412 trillones de años, una de 8 caracteres generada con números y letras mayúsculas y minúsculas se rompe en 15 horas, cualquier contraseña de 7 o menos, en 14 segundos (y estos tiempos pueden ser menores si el ataque es múltiple o con máquinas muy potentes). Es recomendable también cambiarlas con cierta periodicidad (entre 30 y 90 días).
- Nombres de usuario administrador: el sistema por defecto propone un primer administrador que tiene como user id=1 y que es buscado sistemáticamente por los atacantes que tratan de usar inyección de SQL. OWASP recomienda que crees de inmediato un segundo administrador y elimines el primero. Evidentemente no se debe llamar ni «admin», ni «administrador», ni «sysadmin», ni cualquier otra cosa que permita deducir que es el administrador, puede ser cualquier palabra o combinación de palabra y números, por favor también de más de 8 caracteres y que no sea de nadie famoso. A ser posible éste nombre no hay que usarlo en ningún otro sitio web público o privado. No lo uses para crear ni publicar artículos dejarías hecho la mitad del trabajo a las máquinas o personas que pueden tumbar tu web. Si el usuario y contraseña no lo creaste con estas pautas, tranquilo, puedes cambiarlo.
- Usuario de edición/publicación de páginas y artículos: éste es el que usarás para publicar páginas y artículos, hoy día tiene su importancia esa «marca personal», elige bien el nombre que quieres que figure publicado en las páginas y usa un username no coincidente, respeta la pauta sobre contraseñas.
- Elimina la posibilidad de registro de usuarios si no se necesita (ajustes > generales), si por alguna razón se necesita busca un modo que exija al usuario utilizar contraseñas seguras (por ejemplo con este plugin) y protege fuertemente el formulario de registro de Spam, usando como mínimo dos herramientas (si por alguna razón una da fallo tu web estaría vendida).
Asegura las carpetas y ficheros
Indico los números que representan los permisos en Linux/Apache recomendados por OWASP:
- wp-config.php
- Deseado: 400
- Funcional: 440, 600, 640
- carpeta uploads
- Deseado: 755
- Funcional: 766, 777 (not recommended)
- ficheros .htaccess
- Deseado: 400
- Funcional: 440, 444, 600, 640
Como pauta general el resto de las carpetas deberían estar con permisos 755 y los ficheros dentro de ellas como 644.
Elimina install.php y readme.html.
Añade ficheros vacíos index.php en las siguientes carpetas:
- wp-includes
- wp-content
- wp-content/plugins
- wp-content/themes
- wp-content/uploads
Deshabilita la edición de ficheros PHP (temas y plugins) mediante la siguiente instrucción en wp-config.php:
1 | define('DISALLOW_FILE_EDIT', true); |
Cambia el código «sal» de wp-config.php
Verás unas líneas con la siguiente estructura:
- define(‘AUTH_KEY’, ‘put your unique phrase here’);
- define(‘SECURE_AUTH_KEY’, ‘put your unique phrase here’);
- define(‘LOGGED_IN_KEY’, ‘put your unique phrase here’);
- define(‘NONCE_KEY’, ‘put your unique phrase here’);
Genera un nuevo juego de códigos aquí: https://api.wordpress.org/secret-key/1.1/salt/, descarga tu config.php , cámbiaselos y súbelo de nuevo.
Plugins y htaccess
- Instalar un Web Application Firewall (WAF) y aplicarle las reglas de seguridad necesarias. Esta es una recomendación muy usual de las auditorías de seguridad; en el caso de OWASP se recomienda instalar el plugin NinjaFirewall, lo he sometido a varios tests y es realmente efectivo contra muchos tipos de amenazas.
- Lo segundo que hay que decir es que tu mejor ayuda a la seguridad es que no instales plugins que no sean estrictamente necesarios, o que tengan poco mantenimiento, tampoco dejes plugins desactivados, el código que conllevan puede abrir la puerta a determinados ataques.
- Utiliza el plugin All In One WP Security & Firewall como mínimo deberán estar en verde los «critical factors» que señala el módulo y tener un security strength checker de más de 200 puntos. Con él aplicarás buena parte de las acciones recomendadas por OWASP, como la de cambiar el prefijo de la base de datos de WordPress (por defecto es wp_ y se debería cambiar por algún otro prefijo corto no deducible).
- Utiliza Rename wp-login.php te permitirá generar un error 404 a quien trate de entrar a wp-login.php la puerta de entrada para cualquier intento de acceso y podrás crear una página de entrada menos evidente (recomiendo más de 16 caracteres con números y letras y que no diga ni login ni wp por ninguna parte).
- Otros módulos que son interesantes son Block Bad Queries (BBQ) y Remove XMLRPC Pingback Ping.
Las posibles reglas de para .htaccess las indico a continuación. Los modulos NinjaFirewall y WP Security habrán generado dentro de ese fichero información muy útil para protegerte y si usas W3 Total Caché para mejorar el rendimiento de tu web también tendrás ese código. Puedes usar todo o parte del siguiente código colocándolo a coontinuación de los códigos automáticos y antes de las lineas estandar del código htaccess propio de wordpress. Se busca evitar exponer demasiada información técnica sobre tu site y su configuración, evitar algunas estrategias de ataque, la mayor parte de este código está introducido para asegurar más un site. No me quiero extender, si queréis ver lo que hace cada linea mirad los comentarios.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | # MIS MODULOS MANUALES <IfModule mod_rewrite.c> RewriteEngine On #you must block the libwww-perl user-agent in your .htaccess file RewriteCond %{HTTP_USER_AGENT} libwww-perl.* RewriteRule .* – [F,L] </IfModule> # X-Powered-By hidden by default nobody will notice if you're using PHP or ASP or whatever Header unset X-Powered-By # Set by default UTF-8 charset AddDefaultCharset UTF-8 # SPAIN language for this files, change it for your needs AddLanguage es-ES .html .htm .css .js # Set the default charset for every feed methods AddCharset utf-8 .atom .css .js .json .rss .vtt .xml # SPAIN timezone, change it for your needs SetEnv TZ Europe/Madrid # Don not allow any pages to be framed - Defends against CSRF Header set X-Frame-Options SAMEORIGIN # Turn on IE8-IE9 XSS prevention tools Header set X-XSS-Protection "1; mode=block" # Prevent mime based attacks Header set X-Content-Type-Options "nosniff" # Disable server sign ServerSignature Off # drop Range header when more than 5 ranges. CVE-2011-3192 SetEnvIf Range (,.*?){5,} bad-range=1 RequestHeader unset Range env=bad-range # enable keep alive Header set Connection keep-alive # Blank User Agents and Referer are blocked RewriteCond %{HTTP_REFERER} ^$ [NC] RewriteCond %{HTTP_USER_AGENT} ^$ [NC] RewriteRule ^(.*)\.(ico|gif|jpe?g|png|bmp|swf)$ - [F,L] # end Blank UA and Referer # a strong set of rules that you can insert into your web site's .htaccess file that will strip URL requests of many dangerous attack injections # from http://www.esecurityplanet.com/open-source-security/top-5-wordpress-vulnerabilities-and-how-to-fix-them.html <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_METHOD} ^(HEAD|TRACE|DELETE|TRACK) [NC] RewriteRule ^(.*)$ - [F,L] RewriteCond %{QUERY_STRING} \.\.\/ [NC,OR] RewriteCond %{QUERY_STRING} boot\.ini [NC,OR] RewriteCond %{QUERY_STRING} tag\= [NC,OR] RewriteCond %{QUERY_STRING} ftp\: [NC,OR] RewriteCond %{QUERY_STRING} http\: [NC,OR] RewriteCond %{QUERY_STRING} https\: [NC,OR] RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR] RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|%3D) [NC,OR] RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [NC,OR] RewriteCond %{QUERY_STRING} ^.*(\[|\]|\(|\)|<|>|ê|"|;|\?|\*|=$).* [NC,OR] RewriteCond %{QUERY_STRING} ^.*("|'|<|>|\|{||).* [NC,OR] RewriteCond %{QUERY_STRING} ^.*(%24&x).* [NC,OR] RewriteCond %{QUERY_STRING} ^.*(%0|%A|%B|%C|%D|%E|%F|127\.0).* [NC,OR] RewriteCond %{QUERY_STRING} ^.*(globals|encode|localhost|loopback).* [NC,OR] RewriteCond %{QUERY_STRING} ^.*(request|select|insert|union|declare).* [NC] RewriteCond %{HTTP_COOKIE} !^.*wordpress_logged_in_.*$ RewriteRule ^(.*)$ - [F,L] </IfModule> # block access to directory listings, plus a set of specific files related to WordPress and the Web server itself # from http://www.esecurityplanet.com/open-source-security/top-5-wordpress-vulnerabilities-and-how-to-fix-them.html Options All -Indexes <files .htaccess> Order allow,deny Deny from all </files> <files readme.html> Order allow,deny Deny from all </files> <files license.txt> Order allow,deny Deny from all </files> <files install.php> Order allow,deny Deny from all </files> <files wp-config.php> Order allow,deny Deny from all </files> <files error_log> Order allow,deny Deny from all </files> <files fantastico_fileslist.txt> Order allow,deny Deny from all </files> <files fantversion.php> Order allow,deny Deny from all </files> <IfModule mod_rewrite.c> ## Measures to block out SQL injection attacks # Esta parte esta copiada de https://gist.github.com/ludo237/5857215 RewriteCond %{QUERY_STRING} ^.*(;|<|>|'|"|"|'|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|cast|set|declare|drop|update|md5|benchmark).* [NC,OR] # ## Block out reference to localhost/loopback/127.0.0.1 in the Query String # RewriteCond %{QUERY_STRING} ^.*(localhost|loopback|127\.0\.0\.1).* [NC,OR] # ## Block out use of illegal or unsafe characters in the Query String variable # RewriteCond %{QUERY_STRING} ^.*(<|>|'|'|%0A|%0D|%27|%3C|%3E|%00).* [NC] # ########## Begin - File injection protection, by SigSiu.net RewriteCond %{REQUEST_METHOD} GET RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [OR] RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [OR] RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC] RewriteRule .* - [F] ########## End - File injection protection # REDIRIGIR VERSION WWW A NO WWW RewriteEngine On RewriteCond %{HTTP_HOST} !^nombredemisitio\.es$ [NC] RewriteRule ^(.*)$ http://nombredemisitio.es/$1 [R=301,L] ## securizando errores PHP para cualquier version en produccion # supress php errors php_flag display_startup_errors off php_flag display_errors off php_flag html_errors off php_value docref_root 0 php_value docref_ext 0 # prevent access to PHP error log <Files PHP_errors.log> Order allow,deny Deny from all Satisfy All </Files> ## fin securizando errores PHP # deny access to the PHP credits and easter eggs RewriteCond %{QUERY_STRING} \=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} [NC] RewriteRule .* - [F] # end deny access to the PHP credits and easter eggs # block comment spam on the web <IfModule mod_security.c> SecFilterEngine On SecFilterScanPOST On SecFilterDefaultAction "deny,nolog,auditlog,status:503" SecFilterSelective POST_PAYLOAD "(guarantee|refinance|free|obligation|cheap|prices|increase|offer|weight|xxx|mortgage|viagra|poker|traffic|discount|medical|casino|lyrics|loan)" </IfModule> # habilitando tipo kml -google earth- AddType application/vnd.google-earth.kml+xml .kml AddType application/vnd.google-earth.kmz .kmz # END MIS MODULOS MANUALES |
Te recomiendo que las reglas de htaccess no las añadas de golpe sino poco a poco probando que el site no dé problemas, cada instalación de WordPress puede ser muy distinta y no comportarse bien. Recuerda que antes de hacer cualquier cambio de ficheros críticos como wp-config.php o .htaccess es conveniente hacer copias de seguridad.
functions.php
Por último no expongas demasiada información cuando hay un error de login, añadiendo esto en el fichero functions.php de la carpeta correspondiente al tema de tu site:
1 2 3 4 | function login_errors_message() { return 'Ooooops!'; } add_filter('login_errors', 'login_errors_message'); |
Cuando tengas tu sitio suficientemente seguro revisa que las cabeceras http no exponen demasiada información técnica sobre tu sitio, puedes mirarlo, por ejemplo, aquí: https://redbot.org/
Y pàsale estos tests:
- http://sitecheck.sucuri.net/
- https://suite.websecurify.com/ (ver el scanner)
Sigue estas recomendaciones y vivirás un poco más feliz.