Bastionado de un servidor web Apache

Implantación de Aplicaciones Web

Curso 2026/2027

2 Bastionado de un servidor web Apache

2.1 Introducción al bastionado (hardening)

El bastionado o endurecimiento de sistemas (hardening) es el conjunto de técnicas, directivas de configuración y buenas prácticas aplicadas a un sistema informático para reducir al mínimo su superficie de exposición y mitigar posibles vulnerabilidades frente a ciberataques.

El servidor web Apache HTTP Server (denominado comúnmente apache2 en distribuciones GNU/Linux de la familia Debian y Ubuntu) es uno de los servidores web más extendidos en Internet. Tras una instalación básica por defecto, el servidor se entrega configurado para maximizar la compatibilidad y facilitar una rápida puesta en marcha. No obstante, esta configuración inicial incluye módulos innecesarios, cabeceras informativas excesivas y directivas permisivas que resultan inseguras en un entorno de producción real.

El objetivo de este documento es proporcionar a los estudiantes y administradores de sistemas una guía profesional, completa y realista para securizar un servidor web Apache. Las medidas descritas están alineadas con los estándares internacionales de referencia de la industria, incluyendo:

2.1.1 Principios fundamentales de seguridad

A la hora de diseñar y mantener la configuración de seguridad de un servidor web debemos guiarnos por cuatro principios clave:

  1. Principio de menor privilegio (Principle of Least Privilege): Cada proceso, usuario y servicio debe ejecutarse exclusivamente con los permisos mínimos e indispensables para desempeñar su función. El servidor web nunca debe atender peticiones HTTP/HTTPS bajo el superusuario root.
  2. Minimización de la superficie de ataque (Attack Surface Reduction): Todo módulo, servicio, método HTTP, puerto o funcionalidad que no sea estrictamente imprescindible debe ser deshabilitado o desinstalado.
  3. Defensa en profundidad (Defense in Depth): La seguridad no debe depender de una única barrera. Se deben articular múltiples capas superpuestas (cortafuegos de red, control en sistema de ficheros, WAF, TLS robusto, cabeceras HTTP restrictivas y sistemas IPS como Fail2ban).
  4. No confiar en la seguridad por oscuridad (Security by Obscurity): Aunque ocultar versiones e información técnica dificulta el reconocimiento a escáneres automáticos, la verdadera seguridad reside en configuraciones robustas, cifrados actualizados y gestión continua de parches, no en el mero ocultamiento de nombres o rutas.

2.1.2 Principales amenazas contra servidores web

Vector de ataque Descripción Impacto
Reconocimiento (fingerprinting) Identificación de versiones exactas de Apache, sistema operativo y lenguajes backend (PHP). Facilita la búsqueda y ejecución de exploits públicos (CVEs).
Denegación de servicio (DoS / Slowloris) Agotamiento de procesos e hilos de trabajo mediante el envío lento o masivo de cabeceras/peticiones. Caída del servicio e indisponibilidad total para usuarios legítimos.
Fuga de información y path traversal Acceso no autorizado a ficheros del sistema (/etc/passwd), copias de seguridad (.bak, .sql) o ficheros de configuración (.git, .env). Filtración de credenciales de base de datos, código fuente y datos confidenciales.
Ejecución de web shells en subidas Subida y posterior ejecución de scripts maliciosos (.php, .phtml) a través de formularios de subida de archivos. Ejecución remota de código (RCE) y toma de control del servidor.
Ataques de inyección web (SQLi, XSS, LFI) Explotación de vulnerabilidades en el código de la aplicación web alojada. Extracción de bases de datos, alteración de la web y robo de sesiones.
Ataques criptográficos a TLS/SSL Uso de protocolos obsoletos (SSLv3, TLS 1.0, TLS 1.1) o cifrados vulnerables (RC4, 3DES, CBC). Interceptación y descifrado de credenciales y datos en tránsito (Man-in-the-Middle).
Clickjacking / MIME-sniffing Incrustación del sitio en marcos invisibles (iframes) o manipulación del tipo de contenido por el navegador. Secuestro de clics, phishing y ejecución de scripts camuflados.

3.1 Arquitectura y archivos de configuración en Debian/Ubuntu

En las distribuciones GNU/Linux de la familia Debian/Ubuntu, Apache organiza su configuración en el directorio /etc/apache2/ siguiendo una arquitectura modular y jerárquica gestionada mediante enlaces simbólicos:

/etc/apache2/
├── apache2.conf                # Archivo de configuración maestro principal
├── ports.conf                  # Definición de puertos de escucha (Listen 80, 443)
├── envvars                     # Variables de entorno del proceso (usuario, grupo, logs)
├── magic                       # Firmas para detección de tipos MIME
├── conf-available/             # Fragmentos de configuración global disponibles
│   ├── security.conf           # Parámetros de seguridad generales (ServerTokens, etc.)
│   └── security-headers.conf   # Cabeceras de seguridad HTTP personalizadas
├── conf-enabled/               # Enlaces simbólicos a conf-available/ activos
├── mods-available/             # Módulos compilados disponibles en el sistema
├── mods-enabled/               # Enlaces simbólicos a mods-available/ activos
├── sites-available/            # Archivos de configuración de VirtualHosts disponibles
│   ├── 000-default.conf        # VirtualHost HTTP por defecto (o catch-all)
│   └── default-ssl.conf        # VirtualHost HTTPS por defecto
└── sites-enabled/              # Enlaces simbólicos a sites-available/ activos

3.1.1 Comandos de administración de Apache

Para gestionar configuraciones, módulos y sitios web de forma limpia y mantenible, se utilizan las herramientas nativas de Debian/Ubuntu:

# Gestión de sitios web (VirtualHosts)
sudo a2ensite <nombre-sitio>.conf
sudo a2dissite <nombre-sitio>.conf

# Gestión de módulos
sudo a2enmod <nombre-modulo>
sudo a2dismod <nombre-modulo>

# Gestión de fragmentos de configuración global
sudo a2enconf <nombre-configuracion>
sudo a2disconf <nombre-configuracion>

# Comprobación de sintaxis de la configuración (IMPRESCINDIBLE antes de reiniciar)
sudo apache2ctl configtest

# Recarga (sin corte de servicio) o reinicio del servicio
sudo systemctl reload apache2
sudo systemctl restart apache2

[!IMPORTANT] Ejecuta siempre sudo apache2ctl configtest (o sudo apachectl -t) antes de recargar o reiniciar el servicio Apache. Si existe algún error sintáctico o una directiva incompatible, este comando te advertirá de inmediato y evitará una caída inesperada del servicio en producción.

4.1 Mantenimiento del sistema y permisos del sistema de ficheros

4.1.1 Actualización continua del software

El primer paso fundamental en cualquier procedimiento de bastionado consiste en mantener el sistema operativo y el software del servidor actualizados con los últimos parches de seguridad:

sudo apt update
sudo apt upgrade -y

Para consultar la versión instalada de Apache y los módulos compilados de forma estática:

apache2 -v
apachectl -V

4.1.2 Usuario y grupo de ejecución sin privilegios

Cuando el sistema operativo arranca, el proceso maestro de Apache se inicia como superusuario root con el único propósito de enlazar los puertos privilegiados TCP 80 y 443. Inmediatamente después, el servidor genera procesos hijos o hilos de trabajo (worker threads) que se ejecutan bajo un usuario sin privilegios.

En Ubuntu y Debian, este usuario y grupo se configuran en /etc/apache2/envvars:

export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data

Podemos verificar que los procesos de atención de peticiones se ejecutan bajo www-data mediante el comando ps:

ps aux | grep apache2

La salida mostrará un único proceso principal perteneciente a root y múltiples procesos trabajadores pertenecientes a www-data.

[!CAUTION] Nunca cambies APACHE_RUN_USER a root ni permitas que los procesos hijos atiendan peticiones con privilegios de administración. Si un atacante lograra ejecutar código remoto a través de una vulnerabilidad web, obtendría acceso total e inmediato como superusuario a toda la máquina.

4.1.3 Jerarquía de permisos y propiedad en el sistema de ficheros

Un error clásico de administración consiste en otorgar permisos 777 o asignar la propiedad total del DocumentRoot (/var/www/html) al usuario www-data. Si el servidor web tiene permisos de escritura en todos los archivos, un atacante que explote una vulnerabilidad en una aplicación PHP podría sobrescribir ficheros .php, inyectar puertas traseras (web shells) o modificar archivos de configuración.

4.1.3.1 Permisos recomendados para la configuración de Apache (/etc/apache2)

Los archivos de configuración de /etc/apache2 deben pertenecer exclusivamente a root:root y no ser modificables por el usuario www-data:

sudo chown -R root:root /etc/apache2
sudo find /etc/apache2 -type d -exec chmod 755 {} \;
sudo find /etc/apache2 -type f -exec chmod 644 {} \;

[!WARNING] Protección estricta de claves privadas TLS/SSL: Las claves privadas criptográficas (.key) nunca deben tener permisos 644. Si almacenas certificados y claves privadas en /etc/ssl/private o dentro de /etc/apache2/ssl/, asegúrate de que las claves privadas pertenezcan a root:ssl-cert o root:root con permisos 600 o 640, de modo que ningún usuario no autorizado del sistema pueda leerlas.

4.1.3.2 Permisos recomendados para el directorio web (DocumentRoot)

  1. Los directorios deben tener permisos 755 (rwxr-xr-x) o 750 en entornos con múltiples usuarios locales.
  2. Los ficheros regulares deben tener permisos 644 (rw-r--r--) o 640.
  3. El propietario de los archivos debe ser root o un usuario de despliegue administrativo (deploy), perteneciendo al grupo www-data.
  4. Únicamente los directorios destinados explícitamente a la subida de archivos (por ejemplo, uploads/ o media/) deben tener permisos de escritura para www-data.
# Establecer propietario y grupo
sudo chown -R root:www-data /var/www/html

# Ajustar permisos estándar para directorios y archivos
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;

# Directorios de subida específicos que requieren escritura:
sudo chown -R www-data:www-data /var/www/html/uploads
sudo chmod -R 755 /var/www/html/uploads

4.1.4 Protección contra la ejecución de scripts en directorios de subidas (uploads)

Una de las técnicas más frecuentes de intrusión consiste en aprovechar un formulario de subida de ficheros para subir un script PHP malicioso (por ejemplo shell.php o c99.php) a la carpeta uploads/ y solicitarlo vía HTTP para obtener una terminal interactiva.

Aunque la carpeta tenga permisos de escritura para www-data, debemos prohibir taxativamente a Apache que ejecute cualquier tipo de script en ese directorio.

Añadimos la siguiente directiva dentro de la configuración del VirtualHost o en /etc/apache2/conf-available/security.conf:

# Bloquear la ejecución de scripts en directorios de subida de archivos
<Directory "/var/www/html/uploads">
    # Desactivar ejecución de scripts CGI e includes
    Options -ExecCGI -Includes
    
    # Denegar acceso directo a cualquier extensión ejecutable
    <FilesMatch "(?i)\.(php|phtml|phar|php3|php4|php5|php7|php8|cgi|pl|py|sh|jsp|asp|exe)$">
        Require all denied
    </FilesMatch>
    
    # Forzar a Apache a tratar cualquier fichero restante como estático
    ForceType application/octet-stream
    <IfModule mod_php.c>
        php_admin_flag engine off
    </IfModule>
</Directory>

Con esta regla, incluso si un atacante consigue subir un fichero .php, cualquier intento de solicitarlo devolverá un error 403 Forbidden y el código nunca será ejecutado por el servidor.

5.1 Ocultación de información del servidor (information disclosure)

Por defecto, Apache incluye en la cabecera HTTP Server y en los mensajes de error el nombre del servidor, la versión exacta de Apache, el sistema operativo de base y las versiones de los módulos instalados (por ejemplo: Apache/2.4.58 (Ubuntu) OpenSSL/3.0.13 PHP/8.3.6). Esta información facilita la fase de reconocimiento (fingerprinting) a posibles atacantes.

Configuración Cabecera HTTP Server emitida Nivel de exposición
Por defecto (Insegura) Server: Apache/2.4.58 (Ubuntu) mod_ssl/2.4.58 OpenSSL/3.0.13 PHP/8.3.6 Inseguro / Crítico (expone versiones de Apache, SO, OpenSSL y PHP)
Bastionada (Segura) Server: Apache Seguro (mínimo permitido por el estándar HTTP)

5.1.1 Configuración de ServerTokens y ServerSignature

Para limitar al mínimo la información expuesta, editamos el archivo /etc/apache2/conf-available/security.conf:

# Opciones disponibles para ServerTokens:
# Full    -> Apache/2.4.58 (Ubuntu) PHP/8.3.6 mod_ssl/2.4.58
# OS      -> Apache/2.4.58 (Ubuntu)
# Minor   -> Apache/2.4
# Min     -> Apache/2.4.58
# Prod    -> Apache (Mínima información permitida por el estándar HTTP)

ServerTokens Prod
ServerSignature Off

5.1.2 Ocultar la cabecera X-Powered-By

Los intérpretes de backend como PHP añaden por defecto la cabecera X-Powered-By: PHP/8.x.x. Debemos desactivarla en el archivo de configuración php.ini correspondiente:

; En /etc/php/8.x/apache2/php.ini o /etc/php/8.x/fpm/php.ini
expose_php = Off

Adicionalmente, podemos forzar a Apache a eliminar cualquier cabecera X-Powered-By residual generada por aplicaciones o proxies inversos mediante mod_headers:

# Requiere: sudo a2enmod headers
Header always unset X-Powered-By
Header unset X-Powered-By

5.1.3 Personalización de páginas de error (ErrorDocument)

Las páginas de error estándar de Apache pueden revelar detalles sobre la estructura interna del servidor y rutas del sistema operativo. Es una buena práctica redirigir los errores comunes a documentos HTML genéricos y amigables:

# En /etc/apache2/conf-available/security.conf o dentro de cada VirtualHost
ErrorDocument 400 /errores/400.html
ErrorDocument 401 /errores/401.html
ErrorDocument 403 /errores/403.html
ErrorDocument 404 /errores/404.html
ErrorDocument 500 /errores/500.html

5.1.4 Eliminación de la página por defecto de Apache

La página predeterminada de instalación (/var/www/html/index.html) indica que el servidor acaba de ser instalado y expone la estructura de archivos de Ubuntu/Debian. Debemos sustituirla por el contenido real de la aplicación o eliminarla:

sudo rm /var/www/html/index.html

6.1 Control de acceso y directivas de directorio seguras

El control de acceso al sistema de archivos es una de las defensas más críticas de Apache. Si no se restringe adecuadamente, un atacante podría solicitar archivos del sistema base fuera del directorio web a través de vulnerabilidades de cruce de directorios (Directory Traversal).

6.1.1 Política restrictiva global por defecto

En el archivo principal /etc/apache2/apache2.conf, debemos asegurarnos de que la directiva para la raíz del sistema de ficheros (/) bloquee todo el tráfico por defecto:

<Directory />
    Options None
    AllowOverride None
    Require all denied
</Directory>

Esta directiva implementa el principio de denegación por defecto (deny all by default): ningún usuario web puede leer absolutamente nada del disco salvo que se autorice explícitamente en un bloque <Directory> posterior.

6.1.2 Configuración restrictiva del DocumentRoot

Para el directorio donde residen las páginas web públicas (/var/www/html), configuramos permisos selectivos:

<Directory /var/www/html>
    Options -Indexes -FollowSymLinks +SymLinksIfOwnerMatch -Includes -ExecCGI
    AllowOverride None
    Require all granted
</Directory>

Analicemos en detalle cada opción de seguridad:

  1. -Indexes: Desactiva el listado automático de directorios. Si un usuario accede a una carpeta que no contiene un fichero index.html o index.php, Apache no mostrará el listado de archivos contenidos, devolviendo un error 403 Forbidden. Esto previene la enumeración de archivos de la aplicación.
  2. -FollowSymLinks +SymLinksIfOwnerMatch: Desactiva el seguimiento incondicional de enlaces simbólicos. Con SymLinksIfOwnerMatch, Apache solo seguirá un enlace simbólico si el propietario del enlace coincide con el propietario del archivo destino, evitando que un usuario web cree un enlace simbólico que apunte a /etc/passwd o /etc/shadow.
  3. -Includes: Desactiva los Server Side Includes (SSI), evitando la inyección de directivas del lado del servidor en archivos HTML.
  4. -ExecCGI: Deshabilita la ejecución de scripts CGI arbitrarios en este directorio.
  5. AllowOverride None: Deshabilita el uso de archivos .htaccess. Los archivos .htaccess reducen el rendimiento del servidor (Apache debe inspeccionar cada directorio en cada petición) y permiten a usuarios locales anular directivas de seguridad. Toda la configuración debe residir en los archivos principales del servidor.

[!WARNING] Si tu aplicación (por ejemplo, WordPress o Drupal) necesita reglas de reescritura de URLs mediante .htaccess, limita AllowOverride únicamente a lo necesario, como AllowOverride FileInfo Limit Options=SymLinksIfOwnerMatch en lugar de AllowOverride All.

6.1.3 Bloqueo de archivos ocultos, ficheros de configuración y copias de seguridad

Es habitual que durante el despliegue de aplicaciones queden expuestos archivos sensibles de control de versiones (.git), variables de entorno (.env), volcados de bases de datos (.sql) o copias de seguridad de editores (.bak, .swp).

Podemos bloquear globalmente el acceso a todos estos patrones añadiendo la siguiente configuración en /etc/apache2/conf-available/security.conf:

# Bloquear acceso a archivos y directorios ocultos (.git, .env, .htaccess)
<FilesMatch "^\.">
    Require all denied
</FilesMatch>

# Excepción explícita para el reto HTTP-01 de Let's Encrypt / Certbot (.well-known)
<Directory "/var/www/html/.well-known">
    Require all granted
</Directory>

# Bloquear extensiones de archivos sensibles, volcados y copias de seguridad
<FilesMatch "(?i)\.(bak|config|sql|fla|psd|ini|log|sh|env|swp|dist|inc|yaml|yml|md|tpl)$">
    Require all denied
</FilesMatch>

6.1.4 Configuración de un VirtualHost por defecto (catch-all / blackhole)

Cuando un atacante escanea rangos de direcciones IP o envía peticiones con una cabecera Host manipulada (Host Header Injection), Apache recurre por defecto al primer VirtualHost cargado en orden alfabético (generalmente 000-default.conf).

Para neutralizar escaneos masivos y evitar fugas de información, se recomienda configurar un VirtualHost por defecto atrapa-todo (catch-all) que capture cualquier petición dirigida directamente a la dirección IP o con un nombre de dominio desconocido, respondiendo de inmediato con un error 403 Forbidden o 400 Bad Request:

# /etc/apache2/sites-available/000-catchall.conf
<VirtualHost *:80>
    ServerName default
    ServerAlias *
    
    # Denegar cualquier petición no dirigida a un dominio autorizado
    <Location />
        Require all denied
    </Location>
    
    ErrorLog ${APACHE_LOG_DIR}/catchall_error.log
    CustomLog ${APACHE_LOG_DIR}/catchall_access.log combined
</VirtualHost>

Posteriormente, habilitamos este sitio como el primero de la lista con sudo a2ensite 000-catchall.conf. De este modo, los VirtualHosts legítimos solo responderán cuando la cabecera Host coincida exactamente con su ServerName o ServerAlias configurado.

7.1 Arquitectura MPM y desactivación de módulos innecesarios

Cada módulo cargado en Apache consume memoria RAM y añade código que puede contener vulnerabilidades de seguridad potenciales. Siguiendo el principio de minimización, debemos auditar y desactivar todos los módulos que no sean indispensables.

7.1.1 Selección del MPM: modernización con mpm_event y PHP-FPM

En versiones modernas de Apache (2.4+) sobre Debian/Ubuntu, la arquitectura recomendada consiste en utilizar el módulo de multiprocesamiento mpm_event junto con PHP-FPM a través de mod_proxy_fcgi, en lugar del módulo obsoleto mpm_prefork con libapache2-mod-php.

Característica mpm_prefork + libapache2-mod-php (Obsoleto) mpm_event + PHP-FPM / proxy_fcgi (Recomendado)
Modelo de concurrencia Proceso pesado dedicado por conexión Hilos asíncronos ligeros sin bloqueo
Consumo de memoria Muy alto (duplica el intérprete PHP en cada proceso) Mínimo y optimizado
Resistencia DoS/Slowloris Muy baja (agotamiento rápido de procesos) Muy alta (descriptores de socket no bloqueantes)
Aislamiento de seguridad PHP se ejecuta en el mismo espacio que Apache PHP aislado en un proceso/socket Unix independiente
Soporte HTTP/2 No compatible Soporte nativo completo

Para migrar a mpm_event y PHP-FPM:

# Deshabilitar módulo PHP clásico y MPM prefork
sudo a2dismod php8.x mpm_prefork

# Habilitar MPM event, proxy FastCGI y configuración PHP-FPM
sudo a2enmod mpm_event proxy proxy_fcgi setenvif
sudo a2enconf php8.x-fpm
sudo systemctl restart apache2

7.1.2 Auditoría de módulos activos

Para listar todos los módulos compilados y cargados actualmente en memoria:

apache2ctl -M

7.1.3 Módulos potencialmente peligrosos o innecesarios

A continuación se detallan los módulos que deben revisarse o desactivarse en la mayoría de entornos de producción:

Módulo Función Riesgo de seguridad Acción recomendada
mod_autoindex Genera listados HTML de directorios. Facilita la enumeración de archivos y carpetas del servidor. Desactivar (a2dismod autoindex).
mod_info Muestra la configuración completa del servidor en /server-info. Fuga crítica de información y rutas internas. Desactivar en producción (a2dismod info).
mod_status Muestra estadísticas y peticiones activas en /server-status. Fuga de IPs de clientes, URLs consultadas y carga del sistema. Restringir por IP o desactivar.
mod_userdir Mapea URLs tipo /~usuario/ al directorio /home/usuario/public_html. Facilita la enumeración de usuarios locales del sistema Linux. Desactivar (a2dismod userdir).
mod_cgi / mod_cgid Ejecuta scripts CGI tradicionales en /cgi-bin/. Riesgo de ejecución de código y consumo excesivo de recursos. Desactivar si se usa PHP-FPM (a2dismod cgi cgid).
mod_include Ejecuta directivas Server Side Includes (SSI). Riesgo de inyección de código del lado del servidor. Desactivar si no se usa (a2dismod include).
mod_dav / mod_dav_fs Protocolo WebDAV para gestión remota de archivos. Riesgo de subida y modificación no autorizada de ficheros. Desactivar si no se requiere (a2dismod dav dav_fs).

Para desactivar los módulos innecesarios de forma segura:

sudo a2dismod autoindex status info userdir cgi cgid include dav dav_fs
sudo systemctl restart apache2

[!CAUTION] Si utilizas el módulo mod_proxy, verifica que la directiva ProxyRequests Off esté establecida a nivel global. Dejar ProxyRequests On convierte tu servidor en un Proxy Abierto (Open Proxy), permitiendo a ciberdelincuentes utilizar tu infraestructura como pasarela anónima para lanzar ataques a terceros.

7.1.4 Configuración segura de mod_status

Si requieres mantener activo mod_status para herramientas de monitorización interna (como Prometheus, Zabbix o Nagios), debes restringir estrictamente el acceso para que solo responda a peticiones desde localhost o desde la IP específica del servidor de monitorización:

# En /etc/apache2/mods-available/status.conf
<IfModule mod_status.c>
    <Location /server-status>
        SetHandler server-status
        Require local
        Require ip 192.168.1.50
    </Location>
    
    ExtendedStatus On
</IfModule>

8.1 Restricción de métodos HTTP

El protocolo HTTP define diversos métodos de petición (GET, POST, HEAD, PUT, DELETE, OPTIONS, TRACE, PATCH, CONNECT). La inmensa mayoría de sitios web estándar únicamente necesitan procesar peticiones GET, POST y HEAD.

8.1.1 Desactivar el método HTTP TRACE (cross-site tracing)

El método TRACE se diseñó para depuración de red: el servidor web devuelve al cliente el contenido exacto de la petición recibida. Sin embargo, este comportamiento permite a un atacante que ejecute código JavaScript (vía XSS) eludir la protección de cookies marcadas como HttpOnly mediante técnicas de Cross-Site Tracing (XST).

Para desactivar el método TRACE en todo el servidor, editamos /etc/apache2/conf-available/security.conf:

TraceEnable Off

8.1.2 Limitar métodos permitidos a nivel de directorio

Podemos restringir de forma estricta los métodos admitidos en el DocumentRoot mediante la directiva <LimitExcept>:

<Directory /var/www/html>
    # Si la aplicación web utiliza APIs o peticiones CORS, incluye OPTIONS
    <LimitExcept GET POST HEAD OPTIONS>
        Require all denied
    </LimitExcept>
</Directory>

Si la aplicación ofrece una API REST que requiere métodos como PUT, DELETE o PATCH, dichos métodos deben autorizarse exclusivamente dentro del bloque <Location /api> correspondiente, manteniéndolos bloqueados en el resto del sitio:

<Location /api>
    <LimitExcept GET POST PUT DELETE PATCH OPTIONS HEAD>
        Require all granted
    </LimitExcept>
</Location>

9.1 Implementación de cabeceras de seguridad HTTP (HTTP security headers)

Las cabeceras de seguridad HTTP son instrucciones que el servidor web envía en la respuesta HTTP para obligar al navegador del cliente a activar mecanismos de protección integrados. Su correcta implementación mitiga ataques tan extendidos como el Clickjacking, Cross-Site Scripting (XSS), inyección de datos y MIME-sniffing.

Para gestionar cabeceras en Apache, es imprescindible habilitar el módulo mod_headers:

sudo a2enmod headers

A continuación, crearemos un archivo de configuración dedicado /etc/apache2/conf-available/security-headers.conf:

# /etc/apache2/conf-available/security-headers.conf
<IfModule mod_headers.c>
    # 1. HTTP Strict Transport Security (HSTS) - Solo sobre conexiones HTTPS
    <If "%{HTTPS} == 'on'">
        Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    </If>

    # 2. Protección contra Clickjacking
    Header always set X-Frame-Options "SAMEORIGIN"

    # 3. Protección contra MIME-Sniffing
    Header always set X-Content-Type-Options "nosniff"

    # 4. Política de Referrer
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # 5. Restricción de APIs y Hardware del Navegador (Permissions-Policy)
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()"

    # 6. Aislamiento de Origen Cruzado (COOP y CORP)
    Header always set Cross-Origin-Opener-Policy "same-origin"
    Header always set Cross-Origin-Resource-Policy "same-origin"

    # 7. Content Security Policy (CSP)
    Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'self'; form-action 'self'; base-uri 'self';"

    # 8. Desactivar el filtro XSS obsoleto y propenso a vulnerabilidades en navegadores antiguos
    Header always set X-XSS-Protection "0"

    # 9. Asegurar Cookies de Sesión (Inyectar Secure, HttpOnly y SameSite)
    Header edit Set-Cookie ^(.*)$ "$1; Secure; HttpOnly; SameSite=Lax"
</IfModule>

Habilitamos la configuración y recargamos Apache:

sudo a2enconf security-headers
sudo apache2ctl configtest
sudo systemctl reload apache2

9.1.1 Descripción detallada de las cabeceras de seguridad

Cabecera HTTP Propósito y mitigación de seguridad
Strict-Transport-Security (HSTS) Obliga al navegador a usar siempre HTTPS durante el tiempo indicado (31536000s = 1 año), impidiendo ataques de degradación SSL Stripping.
X-Frame-Options Evita que el sitio sea incrustado en <iframe> o <frame> externos, neutralizando por completo ataques de Clickjacking.
X-Content-Type-Options Fija el valor nosniff, impidiendo que el navegador intente adivinar el tipo MIME de un fichero y ejecute scripts camuflados como imágenes.
Referrer-Policy Controla qué información de la URL de origen se envía en la cabecera Referer al navegar hacia enlaces externos, protegiendo tokens y rutas sensibles.
Permissions-Policy Desactiva el acceso a APIs de hardware sensibles del cliente como la cámara, el micrófono, el sensor de geolocalización o pasarelas de pago.
Content-Security-Policy (CSP) Especifica exactamente de qué orígenes autorizados el navegador puede cargar scripts, estilos, imágenes y fuentes. Máxima protección frente a XSS e inyecciones.
SameSite / Secure / HttpOnly en Set-Cookie Protege las cookies de sesión contra robo mediante XSS (HttpOnly), interceptación en tránsito (Secure) y ataques CSRF (SameSite=Lax).

9.1.2 Verificación de cabeceras con curl

Podemos comprobar rápidamente desde el terminal que nuestro servidor emite todas las cabeceras correctamente:

curl -I https://midominio.com

10.1 Configuración robusta de SSL/TLS (HTTPS seguro)

El cifrado en tránsito mediante el protocolo HTTPS es obligatorio en la web actual. No obstante, habilitar HTTPS no es suficiente por sí solo: es necesario garantizar que la configuración criptográfica utilice únicamente protocolos y algoritmos seguros.

10.1.1 Habilitación de módulos criptográficos y rendimiento

# Módulos SSL, almacenamiento en caché de sesiones y soporte para HTTP/2
sudo a2enmod ssl
sudo a2enmod socache_shmcb
sudo a2enmod http2

10.1.2 Selección de protocolos y conjuntos de cifrado (cipher suites)

Debemos deshabilitar de forma categórica protocolos vulnerables como SSLv2, SSLv3, TLS 1.0 y TLS 1.1, permitiendo exclusivamente TLS 1.2 y TLS 1.3.

Editamos el archivo global de configuración SSL /etc/apache2/mods-available/ssl.conf o la configuración de nuestro VirtualHost HTTPS:

# Protocolos permitidos (Solo TLS 1.2 y TLS 1.3)
SSLProtocol -all +TLSv1.2 +TLSv1.3

# Cifrados seguros para TLS 1.2 (con secreto perfecto hacia adelante - PFS)
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

# Preferencia de cifrados (en OpenSSL moderno con TLS 1.3 se recomienda off)
SSLHonorCipherOrder off

# Desactivar compresión SSL para neutralizar ataques CRIME / BREACH
SSLCompression off

# Desactivar Session Tickets si no son estrictamente necesarios
SSLSessionTickets off

10.1.3 Grapado OCSP (OCSP stapling)

El grapado OCSP (OCSP stapling) mejora significativamente el rendimiento y la privacidad del usuario. En lugar de que cada navegador contacte con la Autoridad de Certificación (CA) para comprobar si el certificado ha sido revocado, el propio servidor web Apache consulta periódicamente el estado de revocación a la CA y adjunta la respuesta firmada directamente durante el saludo TLS (TLS Handshake).

Añadimos la siguiente configuración a nivel global (en /etc/apache2/conf-available/ssl-stapling.conf):

<IfModule mod_ssl.c>
    SSLUseStapling on
    SSLStaplingCache "shmcb:${APACHE_RUN_DIR}/ssl_stapling(32768)"
    SSLStaplingResponseTime 5
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnUntrusted off
</IfModule>

10.1.4 Habilitación de HTTP/2

El protocolo HTTP/2 ofrece mejoras drásticas de rendimiento (multiplexación de peticiones sobre una única conexión TCP, compresión de cabeceras HPACK) y exige de forma obligatoria el uso de TLS moderno:

# En el archivo de VirtualHost o a nivel global
Protocols h2 http/1.1

10.1.5 Redirección obligatoria de HTTP a HTTPS

Todo el tráfico que llegue al puerto TCP 80 debe ser redirigido inmediatamente al puerto seguro 443 con un código de estado 301 Moved Permanently:

# /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
    ServerName midominio.com
    ServerAlias www.midominio.com
    
    # Redirección permanente a HTTPS preservando la ruta solicitada
    Redirect permanent / https://midominio.com/
</VirtualHost>

11.1 Protección contra denegación de servicio (DoS) y agotamiento de recursos

Un servidor web Apache mal parametrizado puede ser fácilmente inutilizado mediante ataques de Denegación de Servicio basados en el agotamiento de descriptores de sockets, procesos y memoria (Slowloris, Slow HTTP POST o inundaciones de peticiones).

11.1.1 Ajuste de tiempos de espera (timeouts) y conexiones persistentes

En /etc/apache2/apache2.conf, reducimos los tiempos de espera excesivos por defecto:

# Tiempo máximo (en segundos) que Apache esperará para recibir una petición
Timeout 30

# Habilitar conexiones HTTP persistentes (Keep-Alive)
KeepAlive On

# Número máximo de peticiones por conexión persistente
MaxKeepAliveRequests 100

# Tiempo de espera (en segundos) para la siguiente petición en la misma conexión
KeepAliveTimeout 5

11.1.2 Limitación del tamaño de peticiones y cabeceras

Para prevenir desbordamientos de búfer y subidas masivas de datos que consuman el ancho de banda del servidor, establecemos límites estrictos:

# Limita el tamaño del cuerpo de la petición HTTP a 10 MB (10485760 bytes)
# Ajustar según las necesidades reales de subida de la aplicación
LimitRequestBody 10485760

# Limita el tamaño de la línea de petición HTTP (URI) a 4094 bytes
LimitRequestLine 4094

# Limita el número de campos en las cabeceras HTTP a 50
LimitRequestFields 50

# Limita el tamaño máximo de cada campo de cabecera a 8190 bytes
LimitRequestFieldSize 8190

11.1.3 Mitigación de Slowloris con mod_reqtimeout

El ataque Slowloris mantiene abiertas miles de conexiones HTTP enviando cabeceras a una velocidad extremadamente lenta (1 byte cada pocos segundos), agotando todos los hilos de Apache. El módulo nativo mod_reqtimeout neutraliza esta técnica exigiendo un tiempo máximo y una tasa mínima de transferencia de datos:

sudo a2enmod reqtimeout

Configuramos en /etc/apache2/mods-available/reqtimeout.conf:

<IfModule mod_reqtimeout.c>
    # Espera un máximo de 20s a 40s para recibir cabeceras; si la tasa es menor de 500 bytes/s, cancela la conexión
    RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
</IfModule>

11.1.4 Prevención de inundaciones con mod_evasive

mod_evasive proporciona protección frente a ataques de fuerza bruta, escaneo masivo de URLs y ataques DoS HTTP en capa de aplicación.

sudo apt install libapache2-mod-evasive -y

Configuramos /etc/apache2/mods-available/evasive.conf:

<IfModule mod_evasive20.c>
    # Tamaño de la tabla hash interna
    DOSHashTableSize    3097
    
    # Número máximo de peticiones a una misma página en el intervalo (por defecto 1s)
    DOSPageCount        10
    DOSPageInterval     1
    
    # Número máximo de peticiones totales a un mismo sitio en el intervalo (por defecto 1s)
    DOSSiteCount        100
    DOSSiteInterval     1
    
    # Tiempo de bloqueo en segundos para una IP infractora (recibirá error 403 Forbidden)
    DOSBlockingPeriod   60
    
    # Directorio para registrar bloqueos temporales
    DOSLogDir           "/var/log/mod_evasive"
    
    # Lista blanca de IPs que no deben ser bloqueadas (por ejemplo localhost o monitorización)
    DOSWhitelist        127.0.0.1
</IfModule>

[!TIP] Ajuste realista de umbrales en mod_evasive: En sitios web modernos con abundantes elementos estáticos (múltiples archivos CSS, JS, fuentes e imágenes), un único acceso de un usuario legítimo puede disparar más de 40 peticiones simultáneas. Establecer DOSSiteCount por debajo de 50 provocaría falsos positivos (bloqueo a usuarios legítimos). Valores de DOSPageCount 10 y DOSSiteCount 100 ofrecen un equilibrio robusto y realista.

Creamos el directorio de logs con los permisos adecuados:

sudo mkdir -p /var/log/mod_evasive
sudo chown -R www-data:www-data /var/log/mod_evasive
sudo a2enmod evasive
sudo systemctl restart apache2

12.1 Cortafuegos de aplicaciones web (WAF): ModSecurity y OWASP CRS

Un WAF (Web Application Firewall) opera en la capa 7 del modelo OSI (capa de aplicación), inspeccionando en tiempo real tanto el tráfico HTTP entrante como las respuestas salientes del servidor.

Mientras que un cortafuegos tradicional (como UFW o iptables) solo filtra puertos e IPs, ModSecurity analiza el contenido de las peticiones para detectar y bloquear patrones de inyección SQL (SQLi), Cross-Site Scripting (XSS), inclusión de archivos locales y remotos (LFI/RFI), y ataques contra vulnerabilidades de día cero (0-days).

12.1.1 Instalación de ModSecurity

En Ubuntu y Debian instalamos el módulo oficial:

sudo apt install libapache2-mod-security2 -y

12.1.2 Activación del motor de reglas y ciclo de vida en producción

Por defecto, ModSecurity viene configurado en modo solo detección (DetectionOnly). En entornos profesionales se recomienda seguir este procedimiento:

  1. Fase de detección (staging): Mantener SecRuleEngine DetectionOnly durante unos días para registrar las peticiones legítimas y detectar posibles falsos positivos.
  2. Exclusión de falsos positivos: Si alguna regla del CRS bloquea una funcionalidad legítima (como la edición de entradas en WordPress o subida de ficheros JSON), se desactiva puntualmente esa regla mediante SecRuleRemoveById <ID_REGLA>.
  3. Fase de bloqueo (production): Activar el motor en modo de bloqueo SecRuleEngine On.
# Copiar el archivo de plantilla recomendado
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

# Habilitar el motor de bloqueo (SecRuleEngine On)
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf

12.1.3 Integración del OWASP ModSecurity Core Rule Set (CRS)

El OWASP CRS es el conjunto de reglas de seguridad más reconocido y mantenido del mundo para proteger servidores web.

Instalamos el paquete del CRS en el sistema:

sudo apt install modsecurity-crs -y

Verificamos en /etc/apache2/mods-available/security2.conf que las reglas del CRS se carguen adecuadamente:

<IfModule security2_module>
    SecDataDir /var/cache/modsecurity
    IncludeOptional /etc/modsecurity/*.conf
    IncludeOptional /usr/share/modsecurity-crs/*.conf
    IncludeOptional /usr/share/modsecurity-crs/rules/*.conf
</IfModule>

Reiniciamos el servicio:

sudo apache2ctl configtest
sudo systemctl restart apache2

12.1.4 Inspección de registros forenses de ModSecurity

Cualquier ataque bloqueado quedará detalladamente registrado en el archivo de auditoría:

sudo tail -f /var/log/apache2/modsec_audit.log

13.1 Detección y bloqueo de intrusiones con Fail2Ban

Fail2Ban es un sistema de prevención de intrusiones (IPS) que monitoriza continuamente los archivos de registro (logs) de Apache y del sistema operativo. Cuando detecta múltiples intentos de acceso fallidos, escaneos de vulnerabilidades o patrones anómalos procedentes de una misma dirección IP, añade automáticamente una regla en el cortafuegos de red (nftables o iptables) para bloquear el tráfico de esa IP durante un tiempo determinado.

13.1.1 Instalación de Fail2Ban

sudo apt install fail2ban -y

13.1.2 Configuración de jaulas (jails) para Apache

En lugar de modificar el archivo original /etc/fail2ban/jail.conf, creamos una copia de configuración personalizada en /etc/fail2ban/jail.local:

# /etc/fail2ban/jail.local

[DEFAULT]
# Tiempo de baneo por defecto: 1 hora (3600 segundos)
bantime  = 3600
# Ventana de tiempo para contar reintentos: 10 minutos (600 segundos)
findtime = 600
# Número máximo de intentos antes del baneo
maxretry = 5
# IPs en lista blanca que nunca serán bloqueadas
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24

# 1. Protección contra fuerza bruta en autenticación básica HTTP
[apache-auth]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s

# 2. Bloqueo de escáneres maliciosos y bots dañinos
[apache-badbots]
enabled  = true
port     = http,https
logpath  = %(apache_access_log)s
bantime  = 86400
maxretry = 1

# 3. Bloqueo de escaneos a scripts inexistentes (.php, .asp, .cgi)
[apache-noscript]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s
maxretry = 3

# 4. Bloqueo de intentos de desbordamiento de búfer en URIs
[apache-overflows]
enabled  = true
port     = http,https
logpath  = %(apache_error_log)s
maxretry = 2

# 5. Integración con ModSecurity (baneo automático de atacantes detectados por el WAF)
[apache-modsecurity]
enabled  = true
port     = http,https
logpath  = /var/log/apache2/modsec_audit.log
maxretry = 2

Iniciamos y habilitamos el servicio:

sudo systemctl enable fail2ban
sudo systemctl restart fail2ban

13.1.3 Comandos de supervisión de Fail2Ban

Para comprobar el estado de las jaulas y consultar las IPs actualmente bloqueadas:

# Ver estado general y jaulas activas
sudo fail2ban-client status

# Ver detalle de una jaula específica (ej. apache-auth o apache-noscript)
sudo fail2ban-client status apache-auth

# Desbloquear manualmente una IP baneada por error
sudo fail2ban-client set apache-auth unbanip 192.168.1.100

14.1 Gestión, monitorización y auditoría de registros (logs)

Los registros de actividad (logs) constituyen la principal fuente de información forense ante un incidente de seguridad y son indispensables para la monitorización continua.

14.1.1 Formato enriquecido de registros y soporte para proxies inversos

En entornos donde Apache se encuentra tras un balanceador de carga, CDN o proxy inverso (como Cloudflare, HAProxy o Nginx), la IP registrada por defecto sería la del propio proxy. Es fundamental registrar la IP real del cliente activando el módulo mod_remoteip:

sudo a2enmod remoteip

Configuramos en /etc/apache2/conf-available/remoteip.conf:

<IfModule mod_remoteip.c>
    RemoteIPHeader X-Forwarded-For
    # Declarar rangos de confianza de tus proxies/balanceadores internos
    RemoteIPInternalProxy 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.1
</IfModule>

En /etc/apache2/apache2.conf, definimos un formato de log enriquecido que incluya el tiempo de respuesta del servidor en microsegundos (%D):

# Formato combinado avanzado incluyendo tiempo de proceso (%D) y cabeceras completas
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_secure
CustomLog ${APACHE_LOG_DIR}/access.log combined_secure

# Nivel de detalle de errores: en producción se recomienda "warn" o "notice"
LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/error.log

14.1.2 Permisos seguros en el directorio de registros

Los registros nunca deben ser legibles por usuarios no privilegiados del sistema:

sudo chown -R root:adm /var/log/apache2
sudo chmod 750 /var/log/apache2
sudo chmod 640 /var/log/apache2/*.log

14.1.3 Rotación segura con logrotate

Verificamos la configuración de rotación en /etc/logrotate.d/apache2 para evitar el agotamiento del espacio en disco y garantizar la retención adecuada de evidencias para auditorías:

/var/log/apache2/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 640 root adm
    sharedscripts
    postrotate
        /usr/sbin/apache2ctl graceful > /dev/null
    endscript
}

15.1 Auditoría de seguridad y herramientas de verificación

Una vez aplicadas las directivas de bastionado, es imprescindible auditar y validar la eficacia de las medidas adoptadas utilizando herramientas profesionales.

Herramienta Tipo Objetivo principal
curl / httpie Línea de comandos (CLI) Inspección de cabeceras HTTP y códigos de respuesta.
testssl.sh Script bash local (CLI) Análisis exhaustivo de protocolos y suites TLS/SSL.
Nikto Escáner de seguridad Detección de ficheros sensibles y vulnerabilidades web.
Nmap NSE Escáner de red Auditoría de puertos abiertos y métodos HTTP admitidos.
Lynis Auditoría de host Evaluación integral del bastionado del SO y Apache.
SecurityHeaders.com Servicio web online Calificación y diagnóstico de cabeceras HTTP.
SSL Labs (Qualys) Servicio web online Calificación A+ de la configuración y certificados SSL.

15.1.1 Verificación con curl

# Inspeccionar cabeceras HTTP y validar ausencia de versiones
curl -I https://midominio.com

# Probar si el método TRACE está desactivado (debe devolver 403 o 405)
curl -X TRACE https://midominio.com

# Intentar acceder a un archivo sensible bloqueado (debe devolver 403 Forbidden)
curl -I https://midominio.com/.env
curl -I https://midominio.com/.git/config
curl -I https://midominio.com/backup.sql

15.1.2 Auditoría criptográfica con testssl.sh

testssl.sh es una herramienta de software libre que evalúa cualquier servicio TLS para detectar cifrados obsoletos y vulnerabilidades conocidas (Heartbleed, POODLE, ROBOT, DROWN, CRIME):

# Descarga y ejecución de testssl
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
./testssl.sh/testssl.sh https://midominio.com

15.1.3 Escaneo de vulnerabilidades web con Nikto

sudo apt install nikto -y
nikto -h https://midominio.com -ssl

15.1.4 Auditoría completa del sistema con Lynis

sudo apt install lynis -y
sudo lynis audit system

16.1 Lista de verificación de bastionado

A continuación se resume la lista de verificación completa que todo administrador de sistemas debe validar antes del paso a producción:

Nº Medida de bastionado Directiva / Comando clave Archivo afectado
1 Actualización periódica del SO apt update && apt upgrade -y Sistema operativo
2 Proceso bajo usuario sin privilegios APACHE_RUN_USER=www-data /etc/apache2/envvars
3 Permisos restrictivos en DocumentRoot Directorios 755, Ficheros 644, Propietario root:www-data /var/www/html
4 Bloqueo de ejecución en subidas (uploads) <Directory "/uploads"> ForceType application/octet-stream ... conf-available/security.conf
5 Ocultar versión del servidor ServerTokens Prod y ServerSignature Off conf-available/security.conf
6 Ocultar cabeceras backend PHP expose_php = Off / Header always unset X-Powered-By php.ini / security-headers.conf
7 Páginas de error personalizadas ErrorDocument 404 /errores/404.html conf-available/security.conf
8 Denegación global por defecto <Directory /> Require all denied </Directory> apache2.conf
9 Desactivar listado de directorios Options -Indexes -FollowSymLinks +SymLinksIfOwnerMatch apache2.conf o VirtualHost
10 Desactivar .htaccess innecesarios AllowOverride None apache2.conf
11 Bloqueo de ficheros sensibles y ocultos <FilesMatch "^\."> Require all denied </FilesMatch> conf-available/security.conf
12 VirtualHost catch-all / por defecto 000-catchall.conf con Require all denied sites-available/000-catchall.conf
13 Arquitectura moderna con mpm_event a2enmod mpm_event proxy_fcgi y php-fpm mods-enabled/
14 Módulos mínimos e indispensables a2dismod autoindex status info userdir cgi include dav mods-enabled/
15 Desactivar método HTTP TRACE TraceEnable Off conf-available/security.conf
16 Limitar métodos HTTP estándar <LimitExcept GET POST HEAD OPTIONS> Require all denied apache2.conf o VirtualHost
17 Cabeceras de seguridad HTTP HSTS, CSP, X-Frame-Options, X-Content-Type-Options, etc. conf-available/security-headers.conf
18 Cookies de sesión seguras Header edit Set-Cookie ... "Secure; HttpOnly; SameSite=Lax" conf-available/security-headers.conf
19 Cifrado TLS 1.3 y TLS 1.2 moderno SSLProtocol -all +TLSv1.2 +TLSv1.3 mods-available/ssl.conf
20 Grapado OCSP y HTTP/2 SSLUseStapling on y Protocols h2 http/1.1 ssl-stapling.conf / VirtualHost
21 Mitigación DoS y Slowloris mod_reqtimeout y reducción de Timeout 30 apache2.conf / reqtimeout.conf
22 Prevención de abusos y flood mod_evasive con umbrales realistas mods-available/evasive.conf
23 Cortafuegos WAF en capa 7 ModSecurity con OWASP Core Rule Set (SecRuleEngine On) /etc/modsecurity/
24 Prevención de intrusiones con IPS Fail2Ban con jaulas para Apache y WAF /etc/fail2ban/jail.local
25 Registros enriquecidos y rotación Formato combined_secure, permisos 640, logrotate /var/log/apache2/

17.1 Actividades y ejercicios prácticos de laboratorio

A continuación se plantean siete actividades prácticas guiadas para afianzar los conocimientos en una máquina virtual de prácticas con Ubuntu Server.

17.1.1 Ejercicio 1: Auditoría inicial y ocultación de información

  1. Instala el servidor Apache en tu máquina virtual Ubuntu Server (sudo apt install apache2).
  2. Realiza una petición con curl -I http://localhost y anota la cabecera Server y las versiones visibles.
  3. Modifica /etc/apache2/conf-available/security.conf para establecer ServerTokens Prod y ServerSignature Off.
  4. Habilita la configuración si no lo estuviera (sudo a2enconf security), comprueba la sintaxis con apache2ctl configtest y recarga el servicio.
  5. Vuelve a ejecutar curl -I http://localhost y comprueba que la versión exacta y la distribución han desaparecido.

17.1.2 Ejercicio 2: Restricción de permisos, bloqueo de ficheros y protección de subidas

  1. Crea en /var/www/html/ un archivo oculto llamado .env que contenga una clave simulada (DB_PASSWORD=secreto123) y un volcado de base de datos backup.sql.
  2. Crea un directorio /var/www/html/uploads/ y dentro de él crea un script de prueba info.php que contenga <?php phpinfo(); ?>.
  3. Comprueba con el navegador o curl que inicialmente el servidor permite descargar .env y ejecutar info.php.
  4. Añade a /etc/apache2/conf-available/security.conf las reglas <FilesMatch> para bloquear archivos que comiencen por punto y extensiones sensibles (.sql, .bak, .env).
  5. Añade la directiva de protección en <Directory "/var/www/html/uploads"> para denegar la ejecución de scripts PHP y forzar ForceType application/octet-stream.
  6. Recarga Apache y verifica con curl -I http://localhost/.env y curl http://localhost/uploads/info.php que en ambos casos se devuelve un código de estado 403 Forbidden.

17.1.3 Ejercicio 3: Configuración del VirtualHost catch-all y redirección HTTPS

  1. Crea el archivo /etc/apache2/sites-available/000-catchall.conf con una configuración que deniegue todo el tráfico a peticiones dirigidas por IP directa.
  2. Habilita el sitio con sudo a2ensite 000-catchall.conf.
  3. Configura tu VirtualHost principal (por ejemplo misitio.conf) con ServerName misitio.local.
  4. Comprueba que al acceder mediante curl -H "Host: misitio.local" http://localhost se accede a la web, mientras que al acceder mediante curl http://localhost se recibe un error 403 Forbidden.

17.1.4 Ejercicio 4: Implementación y validación de cabeceras de seguridad y cookies

  1. Habilita el módulo de cabeceras (sudo a2enmod headers).
  2. Crea el archivo /etc/apache2/conf-available/security-headers.conf e incluye las cabeceras recomendadas (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Content-Security-Policy, cookie security).
  3. Habilita la configuración (sudo a2enconf security-headers), valida con apache2ctl configtest y reinicia Apache.
  4. Realiza una petición con curl -I http://localhost y verifica que todas las cabeceras aparecen correctamente en la respuesta HTTP.

17.1.5 Ejercicio 5: Mitigación de Slowloris y protección DoS con mod_reqtimeout y mod_evasive

  1. Habilita el módulo reqtimeout (sudo a2enmod reqtimeout).
  2. Configura en apache2.conf un Timeout de 30 segundos y KeepAliveTimeout de 5 segundos.
  3. Ajusta en reqtimeout.conf los tiempos de lectura de cabeceras y cuerpo con tasa mínima de 500 bytes/s.
  4. Instala y configura libapache2-mod-evasive con umbrales adecuados (DOSPageCount 10, DOSSiteCount 100).
  5. Valida la sintaxis y reinicia el servicio.

17.1.6 Ejercicio 6: Despliegue de ModSecurity (WAF) y bloqueo de inyecciones SQL

  1. Instala los paquetes libapache2-mod-security2 y modsecurity-crs.

  2. Activa SecRuleEngine On en /etc/modsecurity/modsecurity.conf.

  3. Verifica la inclusión de las reglas OWASP en /etc/apache2/mods-available/security2.conf y reinicia Apache.

  4. Simula un ataque de inyección SQL enviando la siguiente petición:

    curl "http://localhost/?id=1%20UNION%20SELECT%20null,username,password%20FROM%20users"
  5. Verifica que Apache responde con un error 403 Forbidden y revisa la entrada generada en /var/log/apache2/modsec_audit.log.

17.1.7 Ejercicio 7: Detección y bloqueo activo con Fail2Ban

  1. Instala el paquete fail2ban (sudo apt install fail2ban -y).
  2. Crea /etc/fail2ban/jail.local habilitando las jaulas apache-auth, apache-badbots, apache-noscript y apache-overflows.
  3. Inicia el servicio Fail2ban y verifica su estado con sudo fail2ban-client status.
  4. Simula múltiples peticiones a scripts inexistentes (curl http://localhost/admin.php, curl http://localhost/setup.cgi, etc.) desde otra máquina o usando herramientas de prueba.
  5. Comprueba con sudo fail2ban-client status apache-noscript que la IP atacante ha sido detectada y bloqueada en el cortafuegos.

19 Referencias

20 Licencia

Licencia de Creative Commons
Esta página forma parte del curso Implantación de Aplicaciones Web de José Juan Sánchez Hernández y su contenido se distribuye bajo una licencia Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional.