NAXSI (Nginx Anti XSS & SQL Injection) es un firewall de aplicaciones web (WAF) que funciona como módulo de Nginx. En lugar de depender de una base de firmas, puntúa cada petición según caracteres y palabras que no deberían aparecer en una URL o un formulario (<, ", union, ../...) y la bloquea cuando la puntuación supera un umbral. Por eso bloquea mucho por defecto, y el trabajo del administrador es añadir excepciones (whitelists) para el tráfico legítimo de su aplicación.
En este tutorial compilarás NAXSI como módulo dinámico para el Nginx de Ubuntu 24.04, cargarás las reglas base, protegerás un sitio, usarás el modo aprendizaje para detectar falsos positivos, escribirás whitelists y una regla propia, y comprobarás que los ataques se bloquean con un 403.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Nginx instalado desde los repositorios de Ubuntu y un sitio funcionando (en los ejemplos se usa el sitio por defecto en
/var/www/html). - Unos 500 MB libres en disco para las fuentes y la compilación.
Notael proyecto original
nbs-system/naxsise archivó en 2023. Esta guía usa el fork mantenido por su último desarrollador activo,wargio/naxsi, que es el que recomienda la documentación actual.
Paso 1: Instalar las dependencias de compilación
Ubuntu no empaqueta NAXSI, así que hay que compilarlo contra el código fuente de la misma versión de Nginx que tienes instalada. Instala Nginx (si aún no lo tienes) y las herramientas de compilación:
sudo apt update
sudo apt install nginx build-essential libpcre2-dev zlib1g-dev wget
Comprueba la versión exacta de Nginx, porque el módulo solo se cargará si se compila contra ella:
nginx -v
nginx version: nginx/1.24.0 (Ubuntu)
El Nginx de Ubuntu está compilado con --with-compat, lo que permite cargar módulos dinámicos compilados aparte siempre que la versión coincida.
Paso 2: Descargar NAXSI y las fuentes de Nginx
Trabaja en un directorio temporal dentro de tu home:
mkdir -p ~/naxsi-build && cd ~/naxsi-build
Descarga la última versión de NAXSI con sus dependencias incluidas (libinjection). Consulta la versión actual en la página de releases; en el momento de escribir esta guía es la 1.7:
NAXSI_VERSION=1.7
wget "https://github.com/wargio/naxsi/releases/download/${NAXSI_VERSION}/naxsi-${NAXSI_VERSION}-src-with-deps.tar.gz"
mkdir -p naxsi
tar -C naxsi -xzf "naxsi-${NAXSI_VERSION}-src-with-deps.tar.gz"
Descarga ahora el código fuente de Nginx de nginx.org con la misma versión que devolvió nginx -v. El siguiente comando la extrae automáticamente:
NGINX_VERSION=$(nginx -v 2>&1 | grep -oP 'nginx/\K[0-9.]+')
echo "$NGINX_VERSION"
wget "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz"
tar -xzf "nginx-${NGINX_VERSION}.tar.gz"
Verifica que tienes los dos árboles de código:
ls ~/naxsi-build/naxsi ~/naxsi-build/nginx-*/
Deberías ver naxsi_src y naxsi_rules en el primero, y configure y src en el segundo.
Paso 3: Compilar el módulo dinámico
Configura Nginx con --with-compat y añade NAXSI como módulo dinámico. Solo se compilan los módulos, no un Nginx nuevo:
cd ~/naxsi-build/nginx-*/
./configure --with-compat --add-dynamic-module=../naxsi/naxsi_src
make modules
Durante configure puede aparecer No package 'libinjection' found seguido de Using submodule libinjection. Es normal: usa la copia de libinjection incluida en el tarball.
Comprueba que el módulo se ha generado:
ls -l objs/ngx_http_naxsi_module.so
-rwxrwxr-x 1 your_user your_user 1392584 sep 25 10:12 objs/ngx_http_naxsi_module.so
Paso 4: Instalar el módulo y las reglas
Copia el módulo al directorio de módulos de Nginx en Ubuntu:
sudo install -m 0644 objs/ngx_http_naxsi_module.so /usr/lib/nginx/modules/
Ubuntu carga los módulos a través de ficheros en /etc/nginx/modules-enabled/, que nginx.conf incluye al principio. Crea el fichero de carga siguiendo esa convención:
echo 'load_module modules/ngx_http_naxsi_module.so;' | sudo tee /etc/nginx/modules-available/mod-http-naxsi.conf
sudo ln -s /etc/nginx/modules-available/mod-http-naxsi.conf /etc/nginx/modules-enabled/50-mod-http-naxsi.conf
Copia las reglas de NAXSI a /etc/nginx/naxsi/:
sudo mkdir -p /etc/nginx/naxsi
sudo cp -r ~/naxsi-build/naxsi/naxsi_rules/* /etc/nginx/naxsi/
ls /etc/nginx/naxsi
blocking naxsi_core.rules whitelists
naxsi_core.rules contiene las reglas genéricas (SQLi, XSS, RFI, traversal, subidas). blocking/ tiene reglas adicionales por categoría y whitelists/ excepciones ya preparadas para aplicaciones conocidas como WordPress o Drupal.
Paso 5: Cargar las reglas base
Las reglas globales se declaran con MainRule y deben estar en el contexto http. En Ubuntu, nginx.conf incluye /etc/nginx/conf.d/*.conf dentro de http, así que crea ahí un fichero:
sudo nano /etc/nginx/conf.d/naxsi.conf
# Reglas globales de NAXSI (contexto http)
include /etc/nginx/naxsi/naxsi_core.rules;
Crea también un fragmento reutilizable con las directivas que se aplican a cada location protegida. Así no repites las mismas líneas en cada sitio:
sudo nano /etc/nginx/naxsi/location.rules
SecRulesEnabled;
LearningMode;
LibInjectionSql;
LibInjectionXss;
DeniedUrl "/RequestDenied";
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$UWA >= 8" BLOCK;
CheckRule "$EVADE >= 8" BLOCK;
CheckRule "$UPLOAD >= 5" BLOCK;
CheckRule "$TRAVERSAL >= 5" BLOCK;
CheckRule "$LIBINJECTION_SQL >= 8" BLOCK;
CheckRule "$LIBINJECTION_XSS >= 8" BLOCK;
Qué hace cada directiva:
SecRulesEnabledactiva NAXSI en lalocation. Sin ella el módulo no hace nada.LearningModeconvierte las accionesBLOCKenLOG: registra lo que habría bloqueado sin cortar tráfico. Empezarás así y lo quitarás en el paso 8.LibInjectionSqlyLibInjectionXssactivan la detección de libinjection, que suma puntos a$LIBINJECTION_SQLy$LIBINJECTION_XSS.DeniedUrles la redirección interna a la que se envía una petición bloqueada.- Los
CheckRuleson obligatorios con las reglas base: definen el umbral de cada puntuación.
Notalas reglas internas (id inferior a 1000, como peticiones malformadas o JSON inválido) bloquean incluso en modo aprendizaje.
Paso 6: Proteger un sitio
Edita el bloque server de tu sitio. En este ejemplo, el sitio por defecto:
sudo nano /etc/nginx/sites-available/default
Deja el bloque server como este (ajusta server_name y root a los tuyos):
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm;
server_name your_domain;
location / {
include /etc/nginx/naxsi/location.rules;
try_files $uri $uri/ =404;
}
location /RequestDenied {
internal;
return 403;
}
}
La location /RequestDenied es internal para que nadie pueda pedirla directamente y deducir que hay un WAF delante. Si tu sitio es un proxy inverso, pon el include dentro de la location que contiene el proxy_pass.
Valida la configuración y recarga Nginx:
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Lanza una petición con una comilla doble en un parámetro, algo que la regla 1001 puntúa como SQLi y XSS:
curl -s -o /dev/null -w '%{http_code}\n' 'http://localhost/?q="test'
200
Devuelve 200 porque estás en modo aprendizaje, pero la petición queda registrada en el log de errores de Nginx:
sudo grep NAXSI_FMT /var/log/nginx/error.log | tail -n 1
2026/09/25 10:30:12 [error] 12345#12345: *7 NAXSI_FMT: ip=127.0.0.1&server=localhost&uri=%2F&config=learning&rid=6c1f...&cscore0=$SQL&score0=8&cscore1=$XSS&score1=8&zone0=ARGS&id0=1001&var_name0=q, client: 127.0.0.1, server: your_domain, request: "GET /?q=%22test HTTP/1.1", host: "localhost"
Los campos importantes son config=learning (no se bloqueó), id0 (regla que saltó), zone0 (dónde: ARGS, BODY, URL, HEADERS) y var_name0 (el parámetro).
Paso 7: Detectar falsos positivos y escribir whitelists
Deja el modo aprendizaje activo mientras usas la aplicación de forma normal: navega, envía formularios, inicia sesión, usa el buscador. Si puedes, déjalo así unos días con tráfico real. Después agrupa los eventos por zona, regla y parámetro para ver qué se repite:
sudo grep -oE 'zone0=[^&]*&id0=[0-9]+&var_name0=[^&,]*' /var/log/nginx/error.log | sort | uniq -c | sort -rn | head -n 20
42 zone0=ARGS&id0=1001&var_name0=q
17 zone0=BODY&id0=1302&var_name0=comment
3 zone0=URL&id0=1000&var_name0=
Cada línea frecuente de tráfico legítimo es candidata a una whitelist. Revisa también las URIs completas en el log antes de decidir: una whitelist mal acotada abre un hueco real.
Las whitelists usan la directiva BasicRule wl:<ids> con una zona de coincidencia (mz:) lo más estrecha posible. Crea un fichero para las del sitio:
sudo nano /etc/nginx/naxsi/whitelist-your_domain.rules
# El buscador acepta comillas en el parámetro q, solo en /search
BasicRule wl:1001 "mz:$URL:/search|$ARGS_VAR:q";
# El campo comment del formulario puede contener < y >
BasicRule wl:1302,1303 "mz:$URL:/contact|$BODY_VAR:comment";
Algunos ejemplos de zonas de coincidencia:
| Zona | Significado |
|---|---|
$ARGS_VAR:q | El parámetro q de la query string |
$BODY_VAR:comment | El campo comment de un POST |
$URL:/search | Solo en esa URL exacta |
$HEADERS_VAR:Cookie | La cabecera Cookie |
Combinar $URL con otra zona mediante | aplica ambas condiciones a la vez, que es lo recomendable. Incluye el fichero dentro de la location protegida, justo después del include de location.rules:
location / {
include /etc/nginx/naxsi/location.rules;
include /etc/nginx/naxsi/whitelist-your_domain.rules;
try_files $uri $uri/ =404;
}
Si usas WordPress, Drupal u otra aplicación conocida, empieza por la whitelist correspondiente de /etc/nginx/naxsi/whitelists/ en lugar de escribirla de cero.
Valida y recarga:
sudo nginx -t && sudo systemctl reload nginx
Paso 8: Añadir una regla propia
Las reglas propias se escriben con MainRule (globales) o BasicRule (por location). Usa identificadores altos para no chocar con las reglas incluidas. Esta regla bloquea peticiones cuyo User-Agent contiene sqlmap:
sudo nano /etc/nginx/naxsi/custom.rules
MainRule id:90001 "s:$UWA:8" "str:sqlmap" "mz:$HEADERS_VAR:User-Agent" "msg:sqlmap en el user-agent";
ides el identificador de la regla (usa valores de 5 o más cifras para las tuyas).s:$UWA:8suma 8 puntos a la puntuación$UWA(Unwanted Access), que ya tiene suCheckRule.str:busca una cadena literal;rx:aceptaría una expresión regular.mz:indica dónde buscar.
Inclúyela en el contexto http, junto a las reglas base:
sudo nano /etc/nginx/conf.d/naxsi.conf
# Reglas globales de NAXSI (contexto http)
include /etc/nginx/naxsi/naxsi_core.rules;
include /etc/nginx/naxsi/custom.rules;
Paso 9: Pasar a modo bloqueo
Cuando el log ya no muestre eventos de tráfico legítimo, desactiva el modo aprendizaje comentando la línea en location.rules:
sudo nano /etc/nginx/naxsi/location.rules
SecRulesEnabled;
# LearningMode;
LibInjectionSql;
LibInjectionXss;
Valida y recarga:
sudo nginx -t && sudo systemctl reload nginx
Comprueba que un intento de XSS, uno de SQLi y la regla propia se bloquean:
curl -s -o /dev/null -w '%{http_code}\n' 'http://localhost/?q=<script>alert(1)</script>'
curl -s -o /dev/null -w '%{http_code}\n' "http://localhost/?id=1'+union+select+password+from+users--"
curl -s -o /dev/null -w '%{http_code}\n' -A 'sqlmap/1.8' http://localhost/
403
403
403
Y que el tráfico normal sigue pasando:
curl -s -o /dev/null -w '%{http_code}\n' 'http://localhost/?q=cubepath'
200
En el log, los eventos bloqueados aparecen ahora con config=block:
sudo grep -c 'config=block' /var/log/nginx/error.log
Sigue revisando el log durante los primeros días en modo bloqueo. Cualquier 403 legítimo que reporten tus usuarios se resuelve igual que en el paso 7: identificar regla, zona y parámetro, y añadir una whitelist acotada.
Mantener el módulo tras actualizar Nginx
El módulo está compilado para una versión concreta de Nginx. Las actualizaciones de seguridad de Ubuntu mantienen la versión 1.24.0, pero si en algún momento cambia la versión upstream (por ejemplo, al actualizar a otra versión de Ubuntu o usar el repositorio de nginx.org), Nginx se negará a arrancar hasta que recompiles. Después de actualizar, comprueba siempre:
nginx -v
sudo nginx -t
Si la versión ha cambiado, repite los pasos 2 a 4 con la nueva versión antes de reiniciar Nginx.
Solución de problemas
unknown directive "SecRulesEnabled": el módulo no está cargado. Comprueba que existe /etc/nginx/modules-enabled/50-mod-http-naxsi.conf y que apunta a un fichero con la línea load_module.
module "/usr/lib/nginx/modules/ngx_http_naxsi_module.so" version 1024000 instead of 1026000: el módulo se compiló para otra versión de Nginx. Recompílalo con las fuentes de la versión que muestra nginx -v.
module ... is not binary compatible: falta --with-compat en el ./configure del paso 3. Vuelve a ejecutar configure y make modules.
Peticiones POST con JSON bloqueadas por la regla 15 u 11: NAXSI valida el cuerpo según el Content-Type. Asegúrate de que el cliente envía Content-Type: application/json con un JSON válido; si una ruta concreta usa un formato que NAXSI no entiende, crea una whitelist acotada a esa $URL.
Líneas de log cortadas: Nginx limita la longitud de cada línea del log de errores, así que una petición con muchas coincidencias puede aparecer truncada. Filtra por el campo rid para agrupar los eventos de la misma petición.
Conclusión
Tienes NAXSI compilado como módulo dinámico para el Nginx de Ubuntu 24.04, con las reglas base y libinjection activos, whitelists acotadas para tu aplicación y una regla propia, todo en modo bloqueo. Como siguientes pasos puedes:
- Añadir HTTPS al sitio con Let's Encrypt y Certbot, manteniendo las mismas directivas en el bloque
serverde 443. - Enviar
/var/log/nginx/error.loga tu sistema de logs centralizado y alertar sobre picos deconfig=block. - Complementar el WAF con limitación de peticiones (
limit_req) de Nginx y Fail2ban para bloquear por IP a los clientes que acumulan 403.
