Implementación de WAF con ModSecurity y reglas OWASP en Apache
Introducción
En el panorama actual de amenazas cibernéticas, la seguridad de las aplicaciones web es un pilar fundamental para cualquier infraestructura de hosting. Un Web Application Firewall (WAF) actúa como una barrera de defensa en la capa de aplicación (OSI Layer 7), inspeccionando y filtrando el tráfico HTTP/HTTPS para bloquear ataques como SQL Injection, Cross-Site Scripting (XSS), Path Traversal, y Remote File Inclusion (RFI). ModSecurity, combinado con el conjunto de reglas OWASP Core Rule Set (CRS), representa la solución WAF de código abierto más madura y ampliamente adoptada para servidores Apache.
Este artículo proporciona una guía exhaustiva para implementar un WAF con ModSecurity y OWASP CRS en Apache, cubriendo desde la instalación hasta el ajuste fino para entornos de producción con alto rendimiento.
Arquitectura de ModSecurity en Apache
ModSecurity opera como un módulo de Apache (mod_security2) que se integra en el ciclo de procesamiento de peticiones. Su arquitectura se compone de:
- Motor de procesamiento (SecRuleEngine): Decide si analizar, bloquear o solo registrar el tráfico.
- Conjunto de reglas: Secuencias de condiciones y acciones (SecRule, SecAction) que definen el comportamiento.
- Fases de procesamiento: Las reglas se ejecutan en fases específicas (REQUEST, REQUEST_HEADERS, REQUEST_BODY, RESPONSE, RESPONSE_HEADERS, RESPONSE_BODY, LOGGING).
Nota importante: ModSecurity no es un módulo de seguridad "plug-and-play". Sin una configuración y ajuste adecuados, puede generar falsos positivos que bloqueen tráfico legítimo o consuman recursos excesivos.
Instalación de ModSecurity en Apache
Requisitos previos
- Apache 2.4.x con soporte para módulos dinámicos (mod_so).
- Acceso root o sudo al servidor.
- Sistema operativo basado en Debian/Ubuntu o RHEL/CentOS (los comandos varían ligeramente).
Instalación en Debian/Ubuntu
# Actualizar repositorios e instalar dependencias
sudo apt update
sudo apt install -y apache2 apache2-dev libapache2-mod-security2
# Habilitar el módulo
sudo a2enmod security2
# Verificar que el módulo esté cargado
sudo apachectl -M | grep security
Instalación en RHEL/CentOS 7/8
# Instalar EPEL y repositorios necesarios
sudo yum install -y epel-release
sudo yum install -y httpd httpd-devel mod_security
# Para CentOS 8/RHEL 8, usar dnf
sudo dnf install -y httpd httpd-devel mod_security
# Habilitar y arrancar Apache
sudo systemctl enable httpd
sudo systemctl start httpd
¿Por qué instalar
apache2-devohttpd-devel? Aunque no son estrictamente necesarios para la ejecución, contienen cabeceras y herramientas útiles para compilar módulos adicionales o realizar configuraciones avanzadas.
Verificación de la instalación
Crea un archivo phpinfo.php en el DocumentRoot y accede a él. Busca la sección "mod_security2" en la salida. Alternativamente:
# Crear un archivo de prueba
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
# Forzar una petición con un payload malicioso para probar
curl -X GET "http://localhost/info.php?param=<script>alert('XSS')</script>"
Si ModSecurity está activo pero sin reglas, verás la respuesta normal. Posteriormente, con reglas cargadas, esta petición será bloqueada.
Configuración Inicial de ModSecurity
El archivo de configuración principal se encuentra en /etc/modsecurity/modsecurity.conf (Debian) o /etc/httpd/conf.d/mod_security.conf (RHEL). La configuración mínima recomendada es:
# /etc/modsecurity/modsecurity.conf
# Activar el motor. "On" para producción, "DetectionOnly" para pruebas.
SecRuleEngine On
# Configurar el directorio de logs
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsec_audit.log
# Configurar almacenamiento de datos persistentes
SecDataDir /tmp/modsecurity/data
# Tamaño máximo del cuerpo de la petición (en bytes)
SecRequestBodyLimit 13107200
# Tamaño máximo de archivos subidos (en bytes)
SecRequestBodyInMemoryLimit 131072
# Tiempo de espera para leer el cuerpo de la petición
SecRequestBodyReadTimeout 10
# Configuración de respuesta
SecResponseBodyLimit 524288
SecResponseBodyMimeType text/plain text/html text/xml application/json
# Desactivar la inspección de imágenes y otros binarios
SecRuleRemoveById 200000
Explicación de directivas clave
| Directiva | Valor | Propósito |
|---|---|---|
SecRuleEngine On | On/DetectionOnly/Off | En producción, "On" bloquea. "DetectionOnly" solo logea. |
SecAuditEngine RelevantOnly | RelevantOnly/On/Off | Logea solo peticiones con código de estado >= 4xx o 5xx. |
SecRequestBodyLimit | 13107200 (13 MB) | Evita ataques de denegación de servicio por cuerpos enormes. |
SecResponseBodyMimeType | Lista de MIME | Evita inspeccionar binarios (imágenes, videos) para ahorrar CPU. |
SecDataDir | Ruta | Directorio para almacenar datos persistentes (IPs bloqueadas, etc.). |
Alerta de rendimiento:
SecResponseBodyAccess On(por defecto) permite a ModSecurity leer el cuerpo de la respuesta. Si tienes aplicaciones que generan grandes respuestas HTML/JSON, considera desactivarlo (Off) o limitar los tipos MIME inspeccionados.
Instalación de OWASP Core Rule Set (CRS)
OWASP CRS es el conjunto de reglas de referencia para ModSecurity. Proporciona protección contra las vulnerabilidades del OWASP Top 10.
Descarga y ubicación
# Crear directorio para reglas (ajustar según distribución)
sudo mkdir -p /etc/modsecurity/crs
# Descargar la última versión estable (v3.3.x al momento de escribir)
cd /tmp
wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v3.3.5.tar.gz
tar -xzf v3.3.5.tar.gz
# Copiar archivos de configuración y reglas
sudo cp -r coreruleset-3.3.5/rules /etc/modsecurity/crs/
sudo cp coreruleset-3.3.5/crs-setup.conf.example /etc/modsecurity/crs/crs-setup.conf
Configuración del CRS
Edita /etc/modsecurity/crs/crs-setup.conf y ajusta los parámetros según tu entorno. Las opciones más relevantes son:
# /etc/modsecurity/crs/crs-setup.conf
# -- Configuración de bloqueo por anomalías --
# Puntuación por defecto para bloquear una petición (anomaly threshold)
SecAction \
"id:900000,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.blocking_paranoia_level=1,\
setvar:tx.executing_paranoia_level=1,\
setvar:tx.anomaly_score_threshold=5,\
setvar:tx.inbound_anomaly_score_threshold=5,\
setvar:tx.outbound_anomaly_score_threshold=4"
# -- Umbral de paranoia --
# Nivel 1: Básico, pocos falsos positivos
# Nivel 2: Moderado, más detección
# Nivel 3: Alto, puede bloquear peticiones legítimas
# Nivel 4: Extremo, solo para pruebas
Incluir el CRS en la configuración de Apache
Crea un archivo de inclusión en el directorio de configuración de Apache:
# /etc/apache2/mods-available/security2.conf (Debian)
# o /etc/httpd/conf.d/mod_security.conf (RHEL)
<IfModule security2_module>
# Configuración base
IncludeOptional /etc/modsecurity/modsecurity.conf
# Configuración del CRS (debe ir antes de las reglas)
IncludeOptional /etc/modsecurity/crs/crs-setup.conf
# Reglas del CRS
IncludeOptional /etc/modsecurity/crs/rules/*.conf
</IfModule>
Orden de inclusión crucial: El archivo
crs-setup.confdebe cargarse ANTES que las reglas individuales, ya que define variables de entorno (tx.*) que las reglas utilizan.
Ajuste Fino y Gestión de Falsos Positivos
Una implementación sin ajustes puede bloquear funcionalidades legítimas. El CRS permite varios mecanismos de personalización.
Paranoia Levels
| Nivel | Descripción | Recomendación |
|---|---|---|
| 1 | Reglas básicas, muy bajo falso positivo | Producción inicial |
| 2 | Añade reglas para ataques más específicos | Producción avanzada |
| 3 | Reglas muy estrictas, puede bloquear tráfico normal | Solo con ajuste fino |
| 4 | Máxima seguridad, bloquea casi todo | Solo pruebas/staging |
Para cambiar el nivel, modifica en crs-setup.conf:
setvar:tx.executing_paranoia_level=2
Reglas de Exclusión (Whitelisting)
Cuando una regla específica bloquea tráfico legítimo (falso positivo), puedes desactivarla o crear una excepción.
# Desactivar una regla específica (ejemplo: regla 942100 - SQL Injection)
SecRuleRemoveById 942100
# Desactivar para una ubicación específica
<LocationMatch "/api/legacy/">
SecRuleRemoveById 942100
</LocationMatch>
# Crear una regla de exclusión condicional
SecRule REQUEST_URI "@beginsWith /wp-admin" \
"id:1000,\
phase:1,\
pass,\
nolog,\
ctl:ruleRemoveById=942100"
Ajuste de Puntuación de Anomalías
El CRS asigna puntuaciones a cada regla. Cuando la suma supera el umbral (anomaly_score_threshold), la petición es bloqueada. Puedes ajustar este umbral:
# Aumentar el umbral para reducir falsos positivos
setvar:tx.inbound_anomaly_score_threshold=10
Monitoreo y Logging
Formato de Logs
ModSecurity genera logs detallados en /var/log/modsec_audit.log (o configurado). Cada entrada tiene un formato estructurado:
--a82b5a5e-A--
[01/Jan/2024:12:00:00 +0000] XXXXXXXXXX 192.168.1.1 12345 192.168.1.100 80
--a82b5a5e-B--
GET /index.php?param=<script> HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
--a82b5a5e-F--
HTTP/1.1 403 Forbidden
--a82b5a5e-H--
Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect|cript)|...)" at ARGS:param. [file "/etc/modsecurity/crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "120"] [id "942100"]
Action: Intercepted (phase 2)
--a82b5a5e-Z--
Herramientas de Análisis
- modsec-helper: Script para parsear logs de auditoría y resumir reglas activadas.
- GoAccess: Puede procesar logs de Apache, pero no logs de ModSecurity directamente. Se recomienda usar
mlogc(ModSecurity Log Collector) para centralizar logs. - ELK Stack: Ideal para entornos empresariales. Envía logs de ModSecurity a Logstash usando el formato JSON.
Para habilitar logs en JSON:
SecAuditLogFormat JSON
SecAuditLog /var/log/modsec_audit.json
Optimización del Rendimiento para Producción
ModSecurity puede consumir hasta un 30% más de CPU si no se optimiza. Estrategias clave:
1. Desactivar la inspección de recursos estáticos
# No inspeccionar imágenes, CSS, JS, etc.
SecRule REQUEST_URI "@endsWith .jpg" "phase:1,id:1,pass,nolog,ctl:ruleEngine=Off"
SecRule REQUEST_URI "@endsWith .png" "phase:1,id:2,pass,nolog,ctl:ruleEngine=Off"
SecRule REQUEST_URI "@endsWith .css" "phase:1,id:3,pass,nolog,ctl:ruleEngine=Off"
SecRule REQUEST_URI "@endsWith .js" "phase:1,id:4,pass,nolog,ctl:ruleEngine=Off"
2. Usar SecRuleEngine DetectionOnly en fases tempranas
Si tienes un balanceador de carga (HAProxy, Nginx) que ya filtra tráfico malicioso, puedes configurar ModSecurity solo para logging en Apache y dejar el bloqueo en el balanceador.
3. Ajustar SecRequestBodyAccess
# Desactivar inspección del cuerpo de peticiones POST si no es necesario
SecRequestBodyAccess Off
Advertencia: Desactivar
SecRequestBodyAccessevita la detección de ataques en el cuerpo POST (como SQLi en formularios). Solo hazlo si tienes otro mecanismo de defensa.
4. Caché de reglas (SecRuleEngine)
ModSecurity no tiene caché de reglas nativo, pero puedes usar mod_cache de Apache para servir páginas cacheadas sin pasar por el WAF.
5. Monitorear con mod_status
# Habilitar status page
<Location "/server-status">
SetHandler server-status
Require ip 127.0.0.1
</Location>
Luego, monitorea el uso de CPU por proceso de Apache. Si ves procesos con alta carga constantemente, revisa los logs de ModSecurity para identificar reglas costosas.
Integración con Entornos de Alta Disponibilidad
En un clúster con balanceo de carga, la configuración de ModSecurity debe ser consistente en todos los nodos. Recomendaciones:
- Sincronizar reglas: Usa un repositorio Git centralizado y despliega con Ansible/Puppet.
- Logs centralizados: Envía logs de auditoría a un servidor central (rsyslog, syslog-ng, Logstash).
- Estado compartido: Si usas bloqueo por IP (mod_evasive o SecRule con IP), considera usar Redis para compartir estado entre nodos.
Ejemplo de configuración con Redis para persistencia compartida
# Instalar módulo de ModSecurity para Redis (requiere compilación)
# En la configuración de reglas
SecCollectionTimeout 600
SecConnEngine On
SecConnReadStateLimit 10
# No hay soporte nativo para Redis en ModSecurity 2.x, pero puedes usar
# scripts externos o migrar a ModSecurity 3 (libmodsecurity3) que sí lo soporta.
Troubleshooting Común
Problema: El sitio se vuelve inaccesible después de activar ModSecurity
# Verificar logs de error de Apache
tail -f /var/log/apache2/error.log
# Temporalmente desactivar el motor
SecRuleEngine DetectionOnly
# Identificar la regla conflictiva
grep "ModSecurity:" /var/log/apache2/error.log
Problema: Alto consumo de memoria
# Reducir el tamaño del búfer de respuesta
SecResponseBodyLimit 131072 # 128 KB
# Desactivar la inspección de respuestas
SecResponseBodyAccess Off
Problema: Reglas que no se aplican
# Verificar que los archivos de reglas tengan permisos de lectura
ls -la /etc/modsecurity/crs/rules/
# Verificar la sintaxis de la configuración
apachectl configtest
Conclusión
Implementar un WAF con ModSecurity y OWASP CRS en Apache es una tarea que requiere
