--- title: "Instalar ModSecurity con OWASP CRS en Apache y Nginx" description: "Instala el WAF ModSecurity con OWASP Core Rule Set en Apache o Nginx sobre Ubuntu 24.04, pasa de modo detección a bloqueo y ajusta los falsos positivos." category: "Seguridad del Servidor" language: es source: https://cubepath.com/es/docs/seguridad-del-servidor/configuracion-de-modsecurity-waf-en-apache-nginx updated: 2026-09-25 --- # Instalar ModSecurity con OWASP CRS en Apache y Nginx ModSecurity es un cortafuegos de aplicaciones web (WAF) de código abierto que inspecciona cada petición HTTP antes de que llegue a tu aplicación. Por sí solo no bloquea nada: la protección la aporta el OWASP Core Rule Set (CRS), un conjunto de reglas que detecta inyección SQL, XSS, inclusión de archivos, ejecución de comandos y escáneres conocidos. En este tutorial instalarás ModSecurity y el CRS desde los repositorios de Ubuntu 24.04, primero en Apache y después en Nginx, empezarás en modo solo detección, activarás el bloqueo y aprenderás a ajustar los falsos positivos. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Apache o Nginx instalado o por instalar. Sigue la sección de tu servidor web: los pasos 1 a 5 son para Apache y el paso 6 para Nginx. Los pasos 7 y 8 sirven para ambos. - El puerto 80 (y el 443 si usas HTTPS) abierto en UFW: `sudo ufw allow 'Apache Full'` o `sudo ufw allow 'Nginx Full'`. Los paquetes que vas a usar son estos: | Paquete | Versión en Ubuntu 24.04 | Uso | |---------|------------------------|-----| | `libapache2-mod-security2` | ModSecurity 2.9.7 | Módulo para Apache | | `libnginx-mod-http-modsecurity` | Conector 1.0.3 con libmodsecurity 3.0.12 | Módulo dinámico para Nginx | | `modsecurity-crs` | OWASP CRS 3.3.5 | Reglas, compartidas por ambos | ## Cómo funciona el CRS El CRS usa un modelo de **puntuación de anomalías**. Cada regla que coincide suma puntos a la petición (5 por una coincidencia crítica, 4 por una de error, 3 por un aviso). Al final de la fase de petición, la regla 949110 compara el total con un umbral, 5 por defecto, y si lo alcanza bloquea la petición con un `403 Forbidden`. Así una sola regla crítica basta para bloquear, pero coincidencias menores aisladas no. El **nivel de paranoia** (1 a 4) decide cuántas reglas se activan. El nivel 1, el predeterminado, tiene pocos falsos positivos y es el adecuado para empezar. ## Paso 1: Instalar ModSecurity en Apache Instala Apache, el módulo y el CRS: ```bash sudo apt update sudo apt install apache2 libapache2-mod-security2 modsecurity-crs ``` El paquete activa el módulo `security2` automáticamente. Compruébalo: ```bash sudo apache2ctl -M | grep security2 ``` ```output security2_module (shared) ``` El archivo `/etc/apache2/mods-enabled/security2.conf` carga todos los `*.conf` de `/etc/modsecurity/` y después el CRS desde `/usr/share/modsecurity-crs/owasp-crs.load`. Por tanto, lo que coloques en `/etc/modsecurity/` se aplica antes que las reglas del CRS. ## Paso 2: Activar la configuración en modo detección El paquete incluye una configuración recomendada que hay que copiar para activarla: ```bash sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf ``` Ábrela y revisa la directiva principal: ```bash sudo nano /etc/modsecurity/modsecurity.conf ``` ```apache SecRuleEngine DetectionOnly ``` Déjala de momento en `DetectionOnly`: ModSecurity evaluará todas las reglas y registrará lo que habría bloqueado, pero no bloqueará nada. Es la forma segura de empezar con una aplicación que ya está en producción. En el mismo archivo verás que el registro de auditoría se guarda en `/var/log/apache2/modsec_audit.log`. Comprueba la sintaxis y recarga Apache: ```bash sudo apache2ctl configtest sudo systemctl reload apache2 ``` ```output Syntax OK ``` ## Paso 3: Comprobar la detección Envía una petición que intenta leer `/etc/passwd`, un patrón típico de inclusión de archivos locales: ```bash curl -s -o /dev/null -w "%{http_code}\n" "http://localhost/?file=/etc/passwd" ``` ```output 200 ``` La petición pasa porque estás en modo detección, pero ModSecurity la ha registrado en el log de errores de Apache: ```bash sudo grep ModSecurity /var/log/apache2/error.log | tail -n 3 ``` ```output [security2:error] [client 127.0.0.1] ModSecurity: Warning. Matched phrase "etc/passwd" at ARGS:file. [file "/usr/share/modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"] [id "930120"] [msg "OS File Access Attempt"] ... [security2:error] [client 127.0.0.1] ModSecurity: Warning. Operator GE matched 5 at TX:anomaly_score. [file "/usr/share/modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [id "949110"] [msg "Inbound Anomaly Score Exceeded (Total Score: 10)"] ... ``` Fíjate en el `id` de cada regla: es lo que usarás para ajustar falsos positivos. Deja el sitio en modo detección un tiempo (unos días con tráfico real) y revisa qué reglas saltan con peticiones legítimas antes de pasar al paso 4. Este comando resume qué reglas se han disparado más veces: ```bash sudo grep -o '\[id "[0-9]*"\]' /var/log/apache2/error.log | sort | uniq -c | sort -rn | head ``` ## Paso 4: Activar el bloqueo Cuando hayas ajustado los falsos positivos (paso 7), cambia la directiva en `/etc/modsecurity/modsecurity.conf`: ```apache SecRuleEngine On ``` Recarga Apache y repite la prueba: ```bash sudo apache2ctl configtest sudo systemctl reload apache2 curl -s -o /dev/null -w "%{http_code}\n" "http://localhost/?file=/etc/passwd" ``` ```output 403 ``` Prueba también un intento de inyección SQL y el agente de usuario de un escáner conocido: ```bash curl -s -o /dev/null -w "%{http_code}\n" "http://localhost/?id=1%27%20OR%20%271%27=%271" curl -s -o /dev/null -w "%{http_code}\n" -A "Nikto" http://localhost/ ``` ```output 403 403 ``` Y confirma que una petición normal sigue funcionando: ```bash curl -s -o /dev/null -w "%{http_code}\n" http://localhost/ ``` ```output 200 ``` ## Paso 5: Revisar el registro de auditoría El registro de auditoría guarda la petición completa (cabeceras y cuerpo) de cada transacción relevante, útil para investigar un bloqueo: ```bash sudo tail -n 40 /var/log/apache2/modsec_audit.log ``` Cada entrada está dividida en secciones marcadas como `--xxxxxxxx-A--`, `-B--` (cabeceras de la petición), `-C--` (cuerpo), `-F--` (cabeceras de la respuesta) y `-H--` (reglas que coincidieron). Ubuntu rota este archivo junto con el resto de `/var/log/apache2/*.log` mediante logrotate. > Nota: el registro de auditoría puede contener datos sensibles enviados por los usuarios, como contraseñas en formularios. Restringe el acceso al archivo y no lo compartas sin revisarlo. ## Paso 6: Instalar ModSecurity en Nginx Si usas Nginx en lugar de Apache, instala el módulo dinámico y el CRS: ```bash sudo apt update sudo apt install nginx libnginx-mod-http-modsecurity modsecurity-crs ``` El paquete carga el módulo mediante `/etc/nginx/modules-enabled/50-mod-http-modsecurity.conf` e instala dos archivos de configuración: `/etc/nginx/modsecurity.conf` (equivalente al `modsecurity.conf` de Apache, en `DetectionOnly` y con el registro de auditoría en `/var/log/nginx/modsec_audit.log`) y `/etc/nginx/modsecurity_includes.conf`, que indica qué reglas cargar. libmodsecurity 3 no admite la directiva `IncludeOptional` que usa el cargador del CRS, así que incluye los archivos del CRS directamente. Edita el archivo de includes: ```bash sudo nano /etc/nginx/modsecurity_includes.conf ``` Sustituye su contenido por este: ```apache include /etc/nginx/modsecurity.conf include /etc/modsecurity/crs/crs-setup.conf include /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf include /usr/share/modsecurity-crs/rules/*.conf include /etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf ``` Activa ModSecurity en el bloque `server` de tu sitio, por ejemplo el predeterminado: ```bash sudo nano /etc/nginx/sites-available/default ``` Añade estas dos líneas dentro del bloque `server { ... }`, antes de los bloques `location`: ```nginx modsecurity on; modsecurity_rules_file /etc/nginx/modsecurity_includes.conf; ``` Comprueba la configuración y recarga Nginx: ```bash sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` Repite las pruebas del paso 3. En modo detección, las coincidencias aparecen en `/var/log/nginx/error.log` con el texto `ModSecurity: Warning`: ```bash curl -s -o /dev/null -w "%{http_code}\n" "http://localhost/?file=/etc/passwd" sudo grep ModSecurity /var/log/nginx/error.log | tail -n 2 ``` Para activar el bloqueo, cambia `SecRuleEngine DetectionOnly` por `SecRuleEngine On` en `/etc/nginx/modsecurity.conf`, ejecuta `sudo nginx -t` y recarga Nginx. La misma prueba debe devolver `403`. ## Paso 7: Ajustar falsos positivos Es normal que algunas reglas salten con peticiones legítimas: un CMS que acepta HTML en un campo, una API que recibe JSON con comillas, un webhook con un cuerpo inusual. La solución nunca es desactivar ModSecurity, sino crear una exclusión lo más precisa posible. Las exclusiones que deben aplicarse antes de evaluar las reglas van en `/etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf`, que tanto Apache como Nginx cargan antes del CRS: ```bash sudo nano /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf ``` Ejemplo 1: la regla 942100 (inyección SQL detectada por libinjection) salta en el parámetro `comment` del formulario de `/contacto`. Excluye solo ese parámetro de esa regla en esa ruta: ```apache SecRule REQUEST_URI "@beginsWith /contacto" \ "id:1000,\ phase:1,\ pass,\ nolog,\ ctl:ruleRemoveTargetById=942100;ARGS:comment" ``` Ejemplo 2: un webhook en `/api/webhook` recibe cuerpos que disparan varias reglas de protocolo. Desactiva solo esas reglas para esa ruta: ```apache SecRule REQUEST_URI "@beginsWith /api/webhook" \ "id:1001,\ phase:1,\ pass,\ nolog,\ ctl:ruleRemoveById=920170,\ ctl:ruleRemoveById=920180" ``` Cada regla necesita un `id` único. Usa el rango 1 a 99999, reservado para reglas locales. Recarga el servidor web después de cada cambio (`sudo systemctl reload apache2` o `sudo systemctl reload nginx`) y repite la petición que fallaba. > Consejo: el CRS incluye exclusiones ya preparadas para WordPress, Drupal, Nextcloud y otras aplicaciones. Se activan en `/etc/modsecurity/crs/crs-setup.conf` descomentando la regla 900130 y poniendo a 1 la variable de tu aplicación, por ejemplo `tx.crs_exclusions_wordpress=1`. ## Paso 8: Ajustar el nivel de paranoia y los umbrales Los ajustes globales del CRS están en `/etc/modsecurity/crs/crs-setup.conf`, compartido por Apache y Nginx: ```bash sudo nano /etc/modsecurity/crs/crs-setup.conf ``` Para subir el nivel de paranoia, busca la regla con `id:900000`, descoméntala y cambia el valor: ```apache SecAction \ "id:900000,\ phase:1,\ nolog,\ pass,\ t:none,\ setvar:tx.paranoia_level=2" ``` Súbelo solo si tu aplicación lo necesita y después de volver a pasar por el modo detección: cada nivel añade reglas y falsos positivos. Los umbrales de anomalía se controlan con la regla `id:900110`. Durante la puesta en marcha puedes usar un umbral alto para bloquear solo los ataques más evidentes e ir bajándolo hasta el valor por defecto (5) a medida que ajustas exclusiones: ```apache SecAction \ "id:900110,\ phase:1,\ nolog,\ pass,\ t:none,\ setvar:tx.inbound_anomaly_score_threshold=10,\ setvar:tx.outbound_anomaly_score_threshold=4" ``` Recarga el servidor web y repite las pruebas del paso 4 para comprobar que los ataques siguen bloqueados. ## Solución de problemas - **Una petición legítima devuelve 403**: busca en el log de errores la línea de la regla 949110 de esa petición y las reglas anteriores con la misma IP y hora. Anota sus `id` y crea una exclusión como en el paso 7. - **Apache no arranca tras un cambio**: ejecuta `sudo apache2ctl configtest`. Un `id` duplicado o una barra invertida de continuación seguida de espacios son los errores más comunes. - **Nginx falla con `IncludeOptional`**: estás cargando `owasp-crs.load` desde Nginx. Usa los `include` explícitos del paso 6. - **Subidas de archivos grandes rechazadas con 413**: `SecRequestBodyLimit` en `modsecurity.conf` limita el tamaño del cuerpo. Súbelo solo lo necesario para tu aplicación. - **El registro de auditoría crece muy rápido**: comprueba que `SecAuditEngine` está en `RelevantOnly` y no en `On`. ## Conclusión Has instalado ModSecurity con el OWASP Core Rule Set en Apache o Nginx, has comprobado la detección, has pasado a modo bloqueo y has visto cómo ajustar falsos positivos con exclusiones precisas y cómo cambiar el nivel de paranoia. Como siguientes pasos, puedes activar HTTPS con Let's Encrypt en el mismo servidor, combinar el WAF con Fail2ban para bloquear en el cortafuegos las IP que acumulan bloqueos y revisar periódicamente el registro de auditoría para detectar nuevos patrones de ataque.