Reforzar la seguridad del sitio: permisos, cabeceras y plugins de seguridad
Módulo 7: Seguridad · Tiempo de lectura: 10 minutos
Objetivo de aprendizaje: Después de esta lección podrá reforzar un sitio WordPress con permisos, ajustes y cabeceras de seguridad, y valorar los plugins de seguridad según su utilidad real.
Conceptos clave: reforzar la seguridad (hardening), permisos de archivos (644, 755), DISALLOW_FILE_EDIT, sin PHP en uploads, listado de directorios, ocultar la versión, X-Powered-By, cabeceras de seguridad, archivos de copia de seguridad y .git en la carpeta web, WAF en el servidor, WAF en un plugin, escáner de malware
Qué significa reforzar la seguridad
Reforzar la seguridad (hardening) significa configurar el sitio de modo que un fallo o una brecha cause el menor daño posible. No sustituye a las actualizaciones ni a las contraseñas seguras (lección 7.2 y 7.3), pero reduce las consecuencias. Piense en una casa: las actualizaciones son las cerraduras, reforzar la seguridad es asegurarse de que un intruso que ya ha entrado no pueda llegar a la caja fuerte.
Permisos de archivos
Cada archivo y cada carpeta del servidor tiene permisos: quién puede leer, escribir y ejecutar. Se escriben como tres cifras, para el propietario, el grupo y el resto del mundo. La recomendación de la documentación de WordPress:
| Qué | Permisos | Significado |
|---|---|---|
| Carpetas | 755 | El propietario puede hacer todo, los demás pueden leer y abrir |
| Archivos | 644 | El propietario puede leer y escribir, los demás solo leer |
| wp-config.php | más estricto, por ejemplo 600 o 640 | Solo el propietario (y, en su caso, el grupo del servidor web) puede leer |
El valor adecuado para wp-config.php depende de cómo ejecute PHP su proveedor de alojamiento: en la mayoría de los alojamientos compartidos PHP se ejecuta como su propio usuario, y en ese caso 600 es suficiente. Pruébelo y compruebe si el sitio sigue funcionando.
Nunca 777. Eso significa: cualquiera en el servidor puede escribir. Un manual que diga «ponga la carpeta en 777 si las subidas no funcionan» resuelve el problema de forma incorrecta. En un buen alojamiento eso tampoco es nunca necesario.
Ajustes en wp-config.php
Unas pocas líneas que marcan mucha diferencia (antes de «Eso es todo, deja de editar»):
// No permitir editar archivos desde el escritorio (Apariencia > Editor de temas y Plugins > Editor de plugins).
define( 'DISALLOW_FILE_EDIT', true );
// Los mensajes de error nunca en pantalla para los visitantes (lección 5.4).
define( 'WP_DEBUG', false );Con DISALLOW_FILE_EDIT desaparece el editor con el que se pueden modificar archivos PHP de temas y plugins desde el escritorio. Un intruso que se haga con una cuenta de administrador no podrá entonces colocar código malicioso en su tema con solo dos clics. Aún podrá instalar plugins; si también quiere impedir eso (por ejemplo en un sitio que administra para un cliente), existe DISALLOW_FILE_MODS, pero entonces tampoco funcionarán las actualizaciones desde el escritorio.
Sin PHP en uploads, sin listado de directorios
En wp-content/uploads deben estar imágenes y documentos, nunca archivos PHP. Los intrusos que consiguen subir un archivo a través de una brecha intentan precisamente colocar un archivo PHP y luego solicitarlo. Bloquéelo con un .htaccess en la carpeta uploads (en LiteSpeed y Apache):
<Files *.php>
Require all denied
</Files>Desactive también el listado de directorios. Sin un archivo index.php o index.html en una carpeta, algunos servidores web muestran una lista de todos los archivos («Index of /wp-content/uploads/2026/11»). Eso es un escaparate para quien busca copias de seguridad o documentos sensibles. En el .htaccess de su carpeta web:
Options -IndexesMuchos proveedores de alojamiento ya lo tienen desactivado de forma predeterminada. El ejercicio de esta lección lo comprueba.
Sin restos en la carpeta web
Todo lo que hay en la carpeta web se puede solicitar a través de la web, salvo lo que el servidor ejecute como PHP o bloquee. Son peligrosos:
- copias de wp-config.php (
wp-config.php.bak,wp-config.old,wp-config.php~de un editor de texto): contienen la contraseña de su base de datos y se envían como texto (lección 1.3); - copias de seguridad como
backup.zip,site.tar.gzodatabase.sqlen la carpeta web; - una carpeta
.gitde un desarrollador, con la que alguien puede descargar todo su código (y a veces contraseñas); - debug.log en wp-content (lección 5.4);
- antiguos archivos de instalación o de prueba como
info.phpconphpinfo().
Guarde las copias de seguridad fuera de la carpeta web o fuera del servidor, y elimine los restos.
Ocultar la versión: una ganancia pequeña
WordPress incluye de forma predeterminada en el HTML:
<meta name="generator" content="WordPress 7.1.4" />y lo mismo en el feed RSS. PHP también se anuncia a veces en una cabecera: X-Powered-By: PHP/8.3.14. Ocultarlo es razonable, pero no espere demasiado: los programas de los intrusos prueban una brecha sin más, sin importar la versión que muestre, y además las versiones suelen poder deducirse igualmente a partir de ?ver= detrás de los scripts. Ocultarlo es ordenar, no es seguridad. Un sitio que oculta su versión pero no se actualiza es igual de vulnerable.
Así se hace, en un pequeño plugin propio o en functions.php de su tema hijo:
remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );Y la cabecera de PHP con expose_php = Off en los ajustes de PHP de su proveedor de alojamiento (en DirectAdmin en Select PHP version, Options), o con Header unset X-Powered-By en .htaccess.
Cabeceras de seguridad
Con unas pocas cabeceras le indica al navegador cómo debe comportarse con su sitio. Las más importantes:
| Cabecera | Qué hace |
|---|---|
Strict-Transport-Security | Solo mediante https (lección 3.1) |
X-Content-Type-Options: nosniff | El navegador no intenta adivinar de qué tipo de archivo se trata; evita que una «imagen» subida se ejecute como script |
X-Frame-Options: SAMEORIGIN o Content-Security-Policy: frame-ancestors 'self' | Otros sitios no pueden mostrar su sitio en un frame; evita el clickjacking (un botón invisible de su sitio bajo el de otro) |
Referrer-Policy: strict-origin-when-cross-origin | Al hacer clic hacia otro sitio, solo se envía su nombre de dominio, no la dirección completa |
Permissions-Policy | Desactiva funciones del navegador que su sitio no utiliza (cámara, micrófono, ubicación) |
Content-Security-Policy | Determina exactamente de qué orígenes pueden proceder scripts y estilos. Potente, pero difícil de configurar bien con WordPress y plugins; empiece con el modo Report-Only |
En .htaccess (en LiteSpeed y Apache):
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>Después compruebe que todo funciona: contenido incrustado, botones de pago, un widget de chat. Herramientas externas como securityheaders.com e internet.nl muestran lo que envía su sitio.
Plugins de seguridad, analizados con honestidad
Existen muchos plugins de seguridad (por ejemplo Wordfence, Solid Security, All-In-One Security, Sucuri). A menudo combinan: un cortafuegos, un escáner de malware, protección de inicio de sesión, verificación en dos pasos (2FA), avisos y una lista de medidas de refuerzo. Útiles, pero con límites:
- Un cortafuegos en un plugin (WAF) solo arranca cuando PHP ya está en marcha. Una solicitud maliciosa debe pasar primero por el servidor web y PHP antes de que el plugin la vea. Una brecha en un archivo PHP al que se accede directamente, o un ataque que satura el propio PHP, no siempre lo detiene. Un cortafuegos en el servidor (por ejemplo ModSecurity, Imunify360 o un cortafuegos de su proveedor de alojamiento) está antes en la cadena y detiene más cosas, sin que su propio paquete tenga que hacer el trabajo de cálculo. Pregunte a su proveedor de alojamiento qué se ejecuta en el servidor.
- Un escáner detecta a posteriori. Un escáner de malware le avisa de que algo va mal. Eso es valioso, pero no evita nada. Y un escáner en el mismo servidor que un sitio hackeado puede haber sido modificado también por el intruso.
- No sustituyen a las actualizaciones. Un plugin con una brecha conocida sigue siendo vulnerable, aunque tenga un plugin de seguridad al lado. Algunos servicios de seguridad ofrecen «parches virtuales» que detienen ataques conocidos hasta que usted actualiza; útil como puente, no como sustituto.
- Son en sí mismos un plugin. Más código, más que actualizar, a veces con su propia brecha. Y dos plugins de seguridad a la vez suelen chocar entre sí.
- Consumen recursos de cálculo, sobre todo los escáneres que se ejecutan en su propio servidor.
Una elección razonable: mire primero qué hace ya su proveedor de alojamiento (cortafuegos, análisis de malware, bloqueo de intentos de inicio de sesión). Complete lo que falte, normalmente la verificación en dos pasos (2FA) y la limitación de intentos de inicio de sesión. Un plugin de seguridad completo resulta especialmente útil con un alojamiento que por sí mismo hace poco.
Resumen
- Reforzar la seguridad limita el daño de un fallo o una brecha; no sustituye a las actualizaciones ni a las contraseñas.
- Carpetas 755, archivos 644, wp-config.php más estricto; nunca 777.
- Active DISALLOW_FILE_EDIT, bloquee PHP en uploads, desactive el listado de directorios y elimine copias de seguridad, .git y debug.log de la carpeta web.
- Ocultar la versión es ordenar, no es seguridad; las cabeceras de seguridad ayudan al navegador a proteger a sus visitantes.
- Un cortafuegos en el servidor detiene más que un plugin; los plugins de seguridad son un complemento, no un sustituto.
Cuestionario
Ejercicio
Ejercicio A7.4: ¿Qué muestra su sitio a todo el mundo? Introduzca la dirección de su sitio. El programa lee la página de inicio y el feed RSS (/feed/) y comprueba: que no aparezca la versión de WordPress en la etiqueta generator del HTML ni del feed, que la cabecera X-Powered-By no muestre una versión de PHP, y que su sitio envíe X-Content-Type-Options: nosniff, una protección contra frames (X-Frame-Options o frame-ancestors) y una Referrer-Policy. Puede faltar HSTS (lección 3.1) y Permissions-Policy (naranja).
Ejercicio A7.4b: Sin restos en la carpeta web. Demuestre que el sitio es suyo (registro TXT o archivo con el código del ejercicio). El programa solicitará a continuación una breve lista fija: la carpeta /wp-content/uploads/ (sin listado de directorios), /.git/HEAD, /wp-content/debug.log y algunos nombres habituales de copias de wp-config.php. Está bien si ninguno de ellos devuelve contenido (404 o 403). El programa no prueba nada más y no modifica nada.
Para saber más
Estos artículos describen los pasos en los paneles de WebOké; en otros paneles funcionan de forma parecida.
- Manual de DirectAdmin 11.5: Permisos de archivos y carpetas (chmod)
- Manual de DirectAdmin 11.4: Mostrar y editar archivos ocultos como .htaccess
- Manual de DirectAdmin 15.10: Firewall de Aplicaciones Web
- Manual de DirectAdmin 15.11: Imunify360: escanear su sitio web en busca de malware
- Bloquear archivos mediante .htaccess, por ejemplo plugin-googlemap2-proxy.php
- Seguridad de PHP
- Reforzar la seguridad de WordPress (developer.wordpress.org, en inglés): https://developer.wordpress.org/advanced-administration/security/hardening/
- Cabeceras de seguridad (MDN, en inglés): https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#security
