Cómo configurar un WAF con Kong Gateway
Cómo añadir protección de tipo WAF a Kong Gateway componiendo sus plugins de seguridad (Injection Protection, JSON/XML Threat Protection, Bot Detection, IP Restriction, Rate Limiting, Request Size Limiting), además de las opciones honestas para lograr cobertura completa del OWASP CRS mediante un WAF en la nube por delante o un plugin comunitario de Coraza.
Kong Gateway es uno de los API gateways de código abierto más desplegados, construido sobre OpenResty y NGINX. Ahora bien, no es un firewall de aplicaciones web basado en firmas o reglas al estilo de ModSecurity o Coraza. El Kong de código abierto no incluye ningún motor del OWASP Core Rule Set (CRS) ni un único "plugin WAF". En su lugar, la propia documentación de Kong plantea el WAF como una función que el gateway cumple componiendo plugins de seguridad: Kong actúa como "una puerta de entrada para tus aplicaciones aplicando autenticación y autorización, imponiendo límites de tasa, restringiendo orígenes abusivos y validando las peticiones antes de que lleguen a los servicios upstream".
Esa distinción importa a la hora de planificar. Algunos de los plugins que se usan aquí (Bot Detection, IP Restriction, Rate Limiting, Request Size Limiting) vienen incluidos con el Kong de código abierto y llevan muchas versiones principales incluidos, por lo que no imponen un piso de versión especial. Los plugins de inspección más profunda a nivel de aplicación (Injection Protection, JSON Threat Protection, XML Threat Protection, Request Validator, Rate Limiting Advanced) son plugins de Kong Enterprise / Konnect, y son ellos los que fijan el verdadero piso de versión de esta guía: Injection Protection necesita Kong Gateway 3.9+, JSON Threat Protection necesita 3.8+ (la validación del cuerpo en POST/PUT/PATCH necesita 3.10+) y XML Threat Protection necesita 3.1+. Si tu plan es seguir la guía completa, apunta a 3.9+ y confirma las licencias con tu suscripción antes de construir sobre ellos.
Esta guía recorre ambas capas con honestidad. Primero endureces Kong con los plugins gratuitos para límite de tasa, control de IP, límites de tamaño y filtrado de bots. Después añades encima los plugins de threat protection de Enterprise para ataques de inyección y de carga útil. Por último, dado que ninguno de ellos ejecuta el OWASP CRS, cubrimos las dos rutas reales hacia una cobertura de nivel CRS: poner un WAF en la nube por delante de un Kong Dedicated Cloud Gateway, o ejecutar un plugin comunitario de Coraza para un Kong autoalojado. La configuración se muestra como configuración declarativa de decK, que también se traslada limpiamente a la Admin API y a Konnect.
Requisitos Previos
- Una instancia de Kong Gateway en funcionamiento (autoalojada, híbrida o Konnect). Usa 3.9+ para la guía completa, ya que Injection Protection requiere Kong Gateway 3.9+; los plugins base de código abierto no imponen un piso de versión especial y llevan incluidos en Kong desde hace muchas versiones principales
- Kong Enterprise o Kong Konnect para los plugins Injection Protection (Kong 3.9+), JSON Threat Protection (Kong 3.8+, validación del cuerpo en POST/PUT/PATCH 3.10+), XML Threat Protection (Kong 3.1+), Request Validator y Rate Limiting Advanced (los plugins base funcionan en el Kong de código abierto)
- Acceso a la Admin API, o la CLI de decK configurada contra tu control plane
- Al menos un Gateway Service y una Route ya proxeando un backend a través de Kong
- Familiaridad básica con las entidades de Kong (Services, Routes, Plugins) y con la configuración declarativa
- curl y una terminal para las pruebas
Guía Paso a Paso
Entiende cómo hace Kong el WAF
Antes de habilitar nada, ten claro qué es Kong y qué no es. Kong es un API gateway construido sobre OpenResty/NGINX. El Kong de código abierto no incluye ningún motor de ModSecurity o Coraza y no ejecuta el OWASP Core Rule Set. La documentación de WAF de Kong describe el WAF como una composición de plugins, no como un único motor de reglas. Existen tres enfoques realistas:
- Componer plugins de seguridad de Kong (esta guía): Encadena Injection Protection, JSON/XML Threat Protection, Bot Detection, IP Restriction, Rate Limiting y Request Size Limiting sobre tus Services y Routes. Esto cubre amenazas comunes de API, pero no es CRS.
- Poner un WAF real por delante del gateway (gestionado/en la nube): Para un Dedicated Cloud Gateway público, coloca por delante un CDN más un WAF en la nube (por ejemplo, las managed rules de AWS WAF en CloudFront) y valida el tráfico de origen. Se cubre en un paso posterior.
- Ejecutar un plugin comunitario de Coraza (autoalojado): Proyectos de la comunidad integran el motor OWASP Coraza y el CRS v4 en Kong como un plugin server. No es un plugin oficial de Kong; tú te haces cargo de la compilación y del soporte. Se cubre en un paso posterior.
Ten en cuenta también la división por tier y versión: Bot Detection, IP Restriction, Rate Limiting y Request Size Limiting están en el Kong de código abierto, mientras que Injection Protection (Kong 3.9+), JSON Threat Protection (Kong 3.8+), XML Threat Protection (Kong 3.1+), Request Validator y Rate Limiting Advanced son plugins de Enterprise / Konnect.
Añade protección básica con los plugins de código abierto
Empieza con los plugins incluidos en el Kong de código abierto. Este kong.yaml declarativo asocia IP Restriction, Request Size Limiting y Bot Detection de forma global, más un Rate Limiting básico sobre un Service:
_format_version: "3.0"
services:
- name: my-api
url: http://upstream:8080
routes:
- name: my-api-route
paths:
- /api
plugins:
# Bloquea cuerpos sobredimensionados (devuelve HTTP 413)
- name: request-size-limiting
config:
allowed_payload_size: 10
size_unit: megabytes
require_content_length: false
# Bloquea user agents maliciosos conocidos por regex (devuelve HTTP 403)
- name: bot-detection
config:
deny:
- "(C)|(c)url"
- "python-requests"
# Deniega rangos de origen abusivos (devuelve HTTP 403)
- name: ip-restriction
config:
deny:
- 203.0.113.0/24
status: 403
message: "Access denied"
Después añade el límite de tasa por cliente. En el Kong de código abierto usa el plugin Rate Limiting; en Enterprise/Konnect prefiere Rate Limiting Advanced por sus ventanas deslizantes y contadores compartidos. Para un único nodo, strategy: local no necesita almacenamiento externo. Para varios nodos, usa strategy: redis, que requiere un bloque config.redis y funciona en despliegues híbridos, DB-less y Konnect:
plugins:
- name: rate-limiting-advanced
service: my-api
config:
limit:
- 100
window_size:
- 60
window_type: sliding
identifier: ip
strategy: redis
sync_rate: 1
redis:
host: redis
port: 6379
Bloquea ataques de inyección con el plugin Injection Protection
El plugin Injection Protection (Enterprise / Konnect, Kong Gateway 3.9+) coteja el contenido de la petición contra patrones regex integrados para clases de inyección. Es lo más parecido a la detección WAF basada en firmas que Kong incluye. Configura qué tipos de inyección y qué ubicaciones de la petición inspeccionar, y empieza en modo log_only:
plugins:
- name: injection-protection
service: my-api
config:
injection_types:
- sql
- js
locations:
- path_and_query
- body
- headers
enforcement_mode: log_only
error_status_code: 400
error_message: "Bad Request"
La referencia de configuración enumera el conjunto completo de injection_types (sql, sql_low_sensitivity, js, java_exception, ssi, xpath_abbreviated, xpath_extended) y de locations (body, headers, path, path_and_query, query). También puedes añadir custom_injections con un name y un regex para patrones específicos de tu aplicación. Ten en cuenta los valores por defecto del plugin: enforcement_mode es block por defecto (no log_only) y locations es [path_and_query] por defecto, así que los ajustes explícitos anteriores sobrescriben ambos. Una copia parcial que los omita bloqueará desde la primera petición y nunca escaneará los cuerpos de las peticiones.
Protege las cargas útiles estructuradas con los plugins de Threat Protection
Las APIs se atacan tanto con cargas sobredimensionadas y anidadas en exceso como con cadenas de inyección. El plugin JSON Threat Protection (Kong 3.8+; la validación del cuerpo en POST/PUT/PATCH requiere Kong 3.10+) impone límites estructurales sobre los cuerpos JSON:
plugins:
- name: json-threat-protection
service: my-api
config:
max_body_size: 1000000
max_container_depth: 20
max_object_entry_count: 100
max_object_entry_name_length: 100
max_array_element_count: 1000
max_string_value_length: 10000
enforce_mode: block
error_status_code: 400
Si aceptas XML, añade el plugin XML Threat Protection (Kong 3.1+), que de forma similar limita la profundidad de elementos, el número de atributos y el tamaño de los valores para defenderte de ataques de XML bomb y de expansión de entidades. Ten en cuenta una diferencia importante entre ambos: JSON Threat Protection admite una fase de solo registro mediante enforce_mode: log_only, así que actívala primero si no estás seguro de la forma de tus cargas legítimas, y luego cambia a enforce_mode: block. XML Threat Protection no tiene modo de aplicación (enforce mode) ni modo de solo registro; siempre bloquea ante una violación de límite, así que valida con cuidado sus límites max_* contra cargas XML reales antes de habilitarlo.
Valida las peticiones contra un esquema
La defensa más fuerte a nivel de API que ofrece Kong es la validación positiva: rechazar todo lo que no coincida con un esquema esperado. El plugin Request Validator (Enterprise / Konnect) valida los cuerpos y parámetros de las peticiones contra un JSON Schema (Draft 4, version: draft4) o el esquema nativo de Kong (version: kong) que proporcionas directamente por Route (opcionalmente generado a partir de una especificación OpenAPI con deck file openapi2kong, una herramienta de decK independiente, no una función del propio plugin):
plugins:
- name: request-validator
route: my-api-route
config:
version: draft4
body_schema: |
{
"type": "object",
"required": ["id"],
"properties": {
"id": { "type": "integer", "minimum": 1 }
},
"additionalProperties": false
}
allowed_content_types:
- application/json
verbose_response: false
Como aplica una lista de permitidos sobre la estructura en lugar de una lista de bloqueo de ataques conocidos, un request validator captura clases enteras de inyección y manipulación de parámetros que el cotejo por firmas se pierde, a cambio de mantener un esquema por Route.
Añade protección de nivel OWASP CRS donde la necesites
Ninguno de los plugins anteriores ejecuta el OWASP Core Rule Set. Si tu cumplimiento normativo o tu modelo de amenazas exige CRS, elige una de las dos rutas honestas:
Ruta gestionada / en la nube (Dedicated Cloud Gateways): Un Dedicated Cloud Gateway público expone un nombre de host DNS, así que pones por delante un CDN capaz de asociar un WAF en la nube. Según la guía de seguridad de red pública de Kong, coloca las managed rules de AWS WAF en una distribución de CloudFront y luego blinda el origen para que el tráfico no pueda saltarse el CDN. CloudFront inyecta una cabecera con un secreto compartido y Kong la exige:
# Rechaza cualquier petición que no haya pasado por el CDN
plugins:
- name: request-termination
config:
status_code: 403
message: "Direct origin access denied"
# ...habilitado mediante una Route que hace match cuando la
# cabecera X-Origin-Verify falta o es incorrecta.
# Añade también a la lista de permitidos los rangos de egress del CDN:
- name: ip-restriction
config:
allow:
- 130.176.0.0/16 # rango de egress de CDN de ejemplo
Kong recomienda almacenar el valor de la cabecera secreta en un Vault y rotarlo periódicamente.
Ruta autoalojada (plugin comunitario de Coraza): Proyectos de la comunidad integran el motor OWASP Coraza y el CRS v4 en Kong como un plugin server en Go, compatible con topologías híbridas, tradicionales y DB-less. Compilas una imagen de Kong personalizada y habilitas el plugin server mediante variables de entorno, por ejemplo:
KONG_PLUGINS=bundled,kong-waf
KONG_PLUGINSERVER_NAMES=kong-waf
KONG_PLUGINSERVER_KONG_WAF_QUERY_CMD="/usr/local/bin/kong-waf -dump"
El plugin se configura luego de forma dinámica a través de la Admin API con directivas SecLang/CRS.
Aplica la configuración
Envía la configuración declarativa a tu control plane con decK, que valida y sincroniza el estado completo:
# Valida primero el archivo
deck gateway validate kong.yaml
# Sincronízalo con Kong (Admin API o control plane de Konnect)
deck gateway sync kong.yaml
También puedes habilitar cada plugin de forma imperativa a través de la Admin API o de la interfaz de Konnect. Confirma que los plugins están activos en tu Service o Route:
curl -s http://localhost:8001/plugins | \
grep -o '"name":"[^"]*"'
Prueba las protecciones
Con Injection Protection cambiado a enforcement_mode: block, envía cargas de ataque conocidas y confirma que cada plugin las rechaza:
# Inyección SQL en la query string (Injection Protection, 400)
curl -i 'http://localhost:8000/api?id=1%20OR%201=1'
# Inyección SQL en un cuerpo JSON (Injection Protection, 400)
curl -i -X POST http://localhost:8000/api \
-H 'Content-Type: application/json' \
-d '{"id":"1 OR 1=1"}'
# User agent de bot denegado (Bot Detection, 403)
curl -i -A 'python-requests/2.31' http://localhost:8000/api
# Cuerpo sobredimensionado (Request Size Limiting, 413)
head -c 20000000 /dev/zero | \
curl -i -X POST --data-binary @- http://localhost:8000/api
# Inundación por encima del límite (Rate Limiting, 429)
for i in $(seq 1 200); do \
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000/api; done
# Una petición normal (debería devolver 200)
curl -i 'http://localhost:8000/api?id=42'
Ajusta y pasa del registro al bloqueo
Ejecuta los plugins de inspección ajustables en modo de solo registro durante una o dos semanas y revisa los logs de Kong en busca de peticiones legítimas que habrían sido rechazadas. Injection Protection emite una línea de log estructurada nombrando el tipo de amenaza, la acción y la ubicación en cada coincidencia, y ahí es donde identificas los falsos positivos.
Ajusta acotando las locations que inspecciona Injection Protection, relajando un límite max_* concreto en los plugins de threat protection, o refinando el body_schema en Request Validator para que el tráfico real valide. Una vez que los logs estén limpios, cambia a block el enforcement_mode de Injection Protection y el enforce_mode de JSON Threat Protection, y vuelve a sincronizar con decK. XML Threat Protection no tiene modo de solo registro y ya bloquea ante cualquier violación, así que ahí no hay nada que cambiar; solo confirma que sus límites max_* coinciden con tu tráfico XML real antes de que entre en producción.
# Tras el ajuste, aplica el bloqueo
deck gateway sync kong.yaml
# Vigila nuevos falsos positivos en los logs
kubectl logs -f deploy/kong-gateway # o logs de docker/systemd
Conclusión y Próximos Pasos
Kong Gateway tiene ahora protección por capas y orientada a API: plugins de código abierto para control de IP, límite de tasa, límites de tamaño y filtrado de bots, más los plugins de threat protection de Enterprise para ataques de inyección y de carga útil, y validación positiva por esquema donde puedes permitirte mantener esquemas. Sé honesto con las partes interesadas sobre el límite: esto es una composición de plugins de seguridad, no el OWASP Core Rule Set, y solo protege el tráfico que pasa por Kong.
Próximos pasos:
- Mantén los plugins de inspección ajustables en modo de solo registro hasta que los logs estén limpios, y luego pasa a bloqueo (XML Threat Protection siempre bloquea, así que valida sus límites de antemano)
- Exporta los logs y métricas de Kong a tu SIEM para que las decisiones de los plugins y los contadores de límite de tasa sean visibles
- Gestiona toda la configuración de plugins como archivos declarativos de decK en el control de versiones y en CI
- Si necesitas cobertura CRS, pon un WAF en la nube por delante de un Dedicated Cloud Gateway o evalúa un plugin comunitario de Coraza, y fija la versión del CRS
- Considera motores WAF de terceros con plugins nativos de Kong (open-appsec, Wallarm) si la detección basada en firmas o ML te importa más que el enfoque de plugins
- Vuelve a probar la batería completa de ataques tras cada actualización de Kong, ya que los valores por defecto y los campos de los plugins pueden cambiar entre versiones
Solución de Problemas
Kong sincroniza pero no se bloquea ningún ataque
Comprueba que los plugins de inspección ajustables están en modo de aplicación, no de registro. Injection Protection usa enforcement_mode: block y JSON Threat Protection usa enforce_mode: block. En modo de solo registro (log_only) Kong anota la coincidencia pero deja pasar la petición. XML Threat Protection no tiene interruptor de modo y siempre bloquea ante una violación de límite, así que si los ataques XML se cuelan el problema es tu configuración de límites, no un ajuste de modo. Confirma también que el plugin está asociado al Service o a la Route que el tráfico realmente alcanza, no a una entidad sin uso.
Los ataques por query string se bloquean pero los del cuerpo no
Injection Protection solo inspecciona las locations que enumeras. Si locations se deja en su valor por defecto path_and_query, los cuerpos de las peticiones nunca se escanean. Añade body (y headers si hace falta) al array locations para que se inspeccionen las cargas POST y JSON.
Peticiones legítimas se rechazan con 400 tras habilitar threat protection
Normalmente un límite max_* de JSON Threat Protection es más estricto que tu tráfico real, por ejemplo max_container_depth o max_string_value_length. En JSON Threat Protection, vuelve a enforce_mode: log_only, muestrea cargas reales, sube el límite concreto que salta y vuelve a aplicar el bloqueo. XML Threat Protection no tiene modo de solo registro, así que sube el límite XML infractor y vuelve a sincronizar directamente. El mismo ajuste aplica a un body_schema demasiado estricto en Request Validator.
El plugin Injection Protection o Request Validator no se habilita
Son plugins de Kong Enterprise / Konnect, y Injection Protection requiere además Kong Gateway 3.9+. En el Kong de código abierto, o en una versión Enterprise más antigua, la Admin API devuelve un error de "plugin not found" o de licencia. Verifica tu suscripción y tu versión de Kong, o recurre a los plugins de código abierto (Bot Detection, IP Restriction, Rate Limiting, Request Size Limiting) más un plugin comunitario de Coraza para una inspección más profunda.
Los recuentos del límite de tasa son incorrectos entre varios nodos
Con Rate Limiting Advanced en strategy: local, cada nodo cuenta de forma independiente, así que un cliente puede superar el límite golpeando distintos data planes. Usa strategy: redis con un bloque config.redis (host y port como mínimo) y un sync_rate adecuado para que los contadores se compartan en toda la flota; esto funciona en despliegues híbridos, DB-less y Konnect. La estrategia cluster también comparte contadores pero solo se admite en modo tradicional (con base de datos), así que no está disponible en híbrido, DB-less ni Konnect: usa redis para esas topologías.
Los atacantes llegan al gateway directamente y saltan el WAF de delante
En un Dedicated Cloud Gateway público, el nombre DNS del origen es accesible salvo que impongas validación de origen. Exige la cabecera inyectada por el CDN (por ejemplo X-Origin-Verify) y rechaza las peticiones que no la traigan mediante Request Termination o un match de Route, y añade a la lista de permitidos los rangos de egress del CDN con el plugin IP Restriction. Almacena y rota el secreto compartido en un Vault.
Bots y escáneres siguen colándose por Bot Detection
El plugin Bot Detection coteja solo la cabecera User-Agent contra regex. Los atacantes que envían un UA de navegador no se detectan. Añade patrones deny personalizados para las firmas que de verdad ves en los logs, y confía en el límite de tasa y en la inspección de inyección para el comportamiento que un UA falsificado no puede ocultar.
Latencia o CPU altas tras habilitar muchos plugins
Cada plugin habilitado se ejecuta en la ruta de la petición dentro del worker de Kong. Los plugins que inspeccionan el cuerpo (Injection Protection, JSON/XML Threat Protection) almacenan en búfer y escanean las cargas, que es el trabajo más costoso. Limita las locations a lo que necesites, acota los tamaños de cuerpo con Request Size Limiting para que las cargas enormes se rechacen antes de la inspección profunda, y acota los plugins pesados a las Routes que los necesitan en lugar de aplicarlos de forma global.
Preguntas Frecuentes
¿Kong Gateway tiene un plugin WAF integrado como ModSecurity o Coraza?
No en el sentido del CRS. El Kong de código abierto no incluye ningún motor de ModSecurity o Coraza y no ejecuta el OWASP Core Rule Set. El enfoque WAF documentado de Kong consiste en componer plugins de seguridad, como Injection Protection, JSON/XML Threat Protection, Bot Detection, IP Restriction y Rate Limiting, formando una puerta de entrada para tus APIs. Para una verdadera cobertura CRS, o pones un WAF en la nube por delante del gateway o ejecutas un plugin comunitario de Coraza.
¿Qué plugins WAF de Kong son gratuitos y cuáles requieren Enterprise?
Bot Detection, IP Restriction, Rate Limiting y Request Size Limiting vienen incluidos con el Kong de código abierto. Injection Protection (Kong 3.9+), JSON Threat Protection (Kong 3.8+), XML Threat Protection (Kong 3.1+), Request Validator y Rate Limiting Advanced son plugins de Kong Enterprise / Konnect. Confirma siempre la disponibilidad del plugin y la versión mínima de Kong con tu suscripción concreta, ya que el tiering y los requisitos de versión pueden cambiar entre releases.
¿Puedo ejecutar el OWASP Core Rule Set con Kong Gateway?
No con los propios plugins de Kong. Dos rutas te dan CRS: poner por delante de un Dedicated Cloud Gateway público un CDN y un WAF en la nube (por ejemplo, las managed rules de AWS WAF en CloudFront) y validar el tráfico de origen, o integrar el motor OWASP Coraza y el CRS v4 en un Kong autoalojado mediante un plugin server de la comunidad. La ruta comunitaria es no oficial y sin soporte de Kong, así que fija la versión del CRS y pruébala contra tu versión de Kong.
¿Cómo protejo un Kong Dedicated Cloud Gateway con un WAF?
Un Dedicated Cloud Gateway público expone un nombre de host DNS, así que colocas por delante un CDN que admita orígenes DNS (como CloudFront) y le asocias un WAF en la nube a la distribución. Para impedir que los atacantes salten el CDN, configura la validación de origen: el CDN inyecta una cabecera con un secreto compartido como X-Origin-Verify, y Kong rechaza las peticiones que no la traigan mediante Request Termination o un match de Route, más IP Restriction en lista de permitidos con los rangos de egress del CDN.
¿En qué se diferencia el Injection Protection de Kong de un motor WAF real?
Injection Protection ofrece cotejo por regex para clases comunes de inyección (SQL, JS, SSI, XPath y más) a través de ubicaciones de petición configurables. Es detección estilo firmas, pero con un conjunto de patrones integrado y fijo, no el OWASP Core Rule Set ajustable y mantenido por la comunidad con niveles de paranoia y puntuación por anomalías. Es eficaz frente a cargas comunes y una buena defensa a nivel de API, pero es más estrecho que un despliegue completo de CRS. Ten en cuenta que es un plugin de Enterprise / Konnect que requiere Kong Gateway 3.9+.
¿Los plugins WAF de Kong añaden latencia?
Los plugins de Kong se ejecutan en proceso, en el mismo worker que gestiona el enrutamiento y la autenticación, así que no hay un salto de proxy adicional, lo que es la principal ventaja de Kong frente a un appliance WAF separado. El coste viene de los plugins que inspeccionan el cuerpo, que lo almacenan en búfer y lo escanean. Limita las ubicaciones inspeccionadas, acota los tamaños de cuerpo con Request Size Limiting antes de la inspección profunda, y acota los plugins pesados a las Routes que los necesitan para mantener baja la sobrecarga.
¿Debería usar los plugins de Kong o un motor WAF de terceros?
Usa los plugins de Kong cuando tu modelo de amenazas sea centrado en API y quieras gestionar la seguridad junto al enrutamiento y la autenticación sin infraestructura adicional. Considera un motor de terceros con un plugin nativo de Kong, como open-appsec (impulsado por ML) o Wallarm (seguridad de API), cuando necesites detección por firmas o por machine learning más allá de lo que ofrecen los plugins integrados. Para CRS en concreto, la ruta es un WAF en la nube por delante o un plugin comunitario de Coraza.