# Modo bajo ataque

> Pon todas las páginas de una zona detrás del reto de navegador durante un ataque a la capa de aplicación y deja que se desactive solo cuando termine.

El modo bajo ataque pone el [reto de navegador](https://developers.openwaf.dev/es/waf/browser-challenge/) delante de toda la zona. Mientras está activado, toda navegación de navegador sin un pase válido recibe la página del reto, diga lo que diga la [barrera](https://developers.openwaf.dev/es/waf/browser-challenge/#gate), y cualquier otra petición sin pase recibe un `403` breve. Los clientes que no pueden ejecutar JavaScript dejan de llegar a tu origen; los visitantes reales esperan un momento, marcan una vez la casilla «Verifica que eres humano» y luego navegan con normalidad.

Viene desactivado, se configura para toda la zona (opcionalmente solo para algunos de sus nombres) y puede desactivarse solo a la hora que elijas.

## Cuándo usarlo

Actívalo mientras dure un ataque que tus reglas habituales no consiguen frenar:

- una **avalancha en la capa de aplicación (L7)**: muchas direcciones pidiendo tus páginas, demasiado repartidas para que el [límite de peticiones](https://developers.openwaf.dev/es/waf/rate-limiting/) o las [reglas de IP y ASN](https://developers.openwaf.dev/es/waf/ip-and-country-rules/) las paren;
- una **oleada de scraping** desde proxies rotatorios o direcciones residenciales.

Fija una [hora de fin](#until) al activarlo, para que no se quede activado cuando el ataque haya pasado.

> **Atención: No es un ajuste permanente**
>
> Cada primera visita espera al reto, y los clientes que no pueden ejecutarlo (clientes de API, apps móviles, webhooks, lectores de feeds, monitores de disponibilidad) reciben `403` salvo que los [exceptúes](#exemptions). Para una protección duradera usa herramientas concretas: la [barrera](https://developers.openwaf.dev/es/waf/browser-challenge/#gate) para formularios de registro y contacto, las [reglas de límite de peticiones](https://developers.openwaf.dev/es/waf/rate-limiting/#per-path-rules) con `action: challenge` para rutas que sufren abuso, las [reglas de IP y ASN](https://developers.openwaf.dev/es/waf/ip-and-country-rules/) para redes conocidas y el [control de rastreadores IA](https://developers.openwaf.dev/es/ai-crawl-control/) para bots.


## Qué ven los visitantes

Una navegación de navegador (un `GET` o `HEAD` cuyo `Accept` incluye `text/html`, sin `Sec-Fetch-Mode` o con `navigate`) sin un pase válido recibe la [página del reto](https://developers.openwaf.dev/es/waf/browser-challenge/#what-the-visitor-sees):

1. Responde `503` con `Retry-After: 30`, `Cache-Control: no-store`, `X-Robots-Tag: noindex` y `OpenWAF-Mitigated: challenge`. Un `HEAD` recibe solo las cabeceras.
2. Dice «Comprobando tu navegador antes de acceder a www.example.com» (en inglés, «Checking your browser before accessing www.example.com»), con la marca de la zona, su color de acento y el idioma del visitante.
3. El navegador resuelve la prueba de trabajo mientras la página indica que está comprobando. Por defecto es un bit más difícil que el reto normal del nombre, lo que duplica el trabajo: normalmente menos de un par de segundos, algo más en móviles lentos.
4. Con el [paso interactivo](#interactive-step) (activado por defecto), la página muestra entonces el nombre del sitio como título, la línea «Verifica que eres humano marcando la casilla.» y una caja de verificación con la casilla «Verifica que eres humano» («Verify you are human» en inglés). El visitante la marca con un clic o un toque, o con Espacio cuando tiene el foco.
5. La casilla se convierte en un indicador de carga con «Verificando…», luego en una marca verde con «Correcto», y la página se recarga: el borde ha puesto la cookie `__waf_pass`. Mientras el pase sea válido, el visitante navega con normalidad. Si la comprobación falla, la caja muestra una cruz roja y un botón «Reintentar».

A diferencia de la barrera, el modo bajo ataque también muestra la página en los nombres `api.*`. Los pases obtenidos antes de activarlo siguen valiendo hasta que caducan.

## Paso interactivo

Con una conexión rápida la prueba de trabajo termina antes de que el visitante se dé cuenta, así que una página que pasara sola parecería que no protege nada, y un script capaz de calcular SHA-256 pasaría igual. Por eso, durante el modo bajo ataque, la página pide por defecto una acción en una caja de verificación (`interactive: true`). Es fricción para los scripts, no una prueba de que haya una persona.

Cuando la prueba de trabajo está resuelta, la página muestra:

- el nombre del sitio como título y «Verifica que eres humano marcando la casilla.» («Verify you are human by completing the action below.» en inglés);
- la **caja de verificación**, la misma que el estado interactivo del [widget Verify](https://developers.openwaf.dev/es/waf/verify-widget/#appearance): la casilla «Verifica que eres humano» a la izquierda y la marca de la zona a la derecha, con el color de acento de la zona y en modo claro u oscuro según prefiera el visitante;
- «www.example.com necesita comprobar la seguridad de tu conexión antes de continuar.»;
- un pie pequeño, «Protegido por OpenWAF | ID: 3f9c2a7b1e604d85» (con marca blanca, el texto de tu agencia en lugar del de OpenWAF).

La caja pasa por estos estados:

| Estado | Qué ve el visitante |
|---|---|
| Inicial | Una casilla vacía con su texto. Es una casilla real: recibe el foco del teclado, muestra un anillo de foco en el color de acento y también se puede pulsar su texto. |
| Verificando | Tras un clic, un toque o Espacio, la casilla se convierte en un indicador de carga (quieto si el visitante pide movimiento reducido) con «Verificando…». |
| Correcto | Una marca verde con «Correcto»; la página se recarga y el visitante sigue al sitio. |
| Fallo | Una cruz roja con un botón «Reintentar», que recibe el foco. |

Cada estado se anuncia a los lectores de pantalla.

- Solo cuenta un evento de entrada de confianza del navegador (un clic, un toque, o Espacio o Intro desde el teclado): un clic sintetizado por un script de la página, como `element.click()`, o marcar `checked` desde un script no es de confianza y no envía nada. El borde también rechaza una resolución que llega menos de medio segundo después de emitirse el reto, lo que frena los reenvíos instantáneos; no hay un tiempo mínimo entre que aparece la caja y el clic, porque quien ya tiene el puntero ahí pulsa en milisegundos. Un intento rechazado recibe `403` con `interaction-required` de `/__waf/verify`, la caja muestra el estado de fallo y cuenta como reto fallido.
- El **ID** del pie identifica esa página de reto (se deriva del reto, no del visitante, y no contiene nada secreto). Si un visitante no consigue pasar, pídeselo: con él, el soporte de OpenWAF encuentra los intentos de verificación de esa página.
- Los navegadores que declaran estar automatizados (`navigator.webdriver`) reciben la misma caja que todo el mundo: no hay huella digital adicional.
- Los pases, su duración y su ámbito no cambian; solo cambia cómo se obtienen.

**Efecto en la automatización.** Los scrapers y las herramientas de avalancha que solo descargan tus páginas, o que las cargan en un navegador sin interfaz y resuelven la prueba de trabajo sin entrada real, ya no obtienen pase. La automatización que inyecta entrada a nivel del navegador o del sistema operativo (por ejemplo Playwright o Puppeteer manejando el protocolo DevTools) genera eventos de confianza y puede pasar, igual que una persona a la que se paga por pulsar. El paso encarece cada pase nuevo, red por red; no demuestra intención humana. Las peticiones que nunca ven la página (API, webhooks, [rutas exceptuadas](#exemptions)) no se ven afectadas en ningún caso.

Pon `interactive` a `false` para volver a la prueba de trabajo sola, de modo que la página pase sola, por ejemplo mientras pruebas el modo en un nombre de pruebas o si tus visitantes usan quioscos donde nadie puede pulsar. Consulta [Activarlo y desactivarlo](#turn-on-and-off).

## API, apps móviles y webhooks

Cualquier otra petición sin pase recibe `403`, nunca la página HTML: llamadas a la API, envíos de formularios, `fetch` y XHR, apps móviles, webhooks, llamadas entre servidores. El cuerpo es JSON cuando `Accept` incluye `json` o la petición es un `fetch` (`Sec-Fetch-Mode` `cors`, `same-origin` o `no-cors`):

```http
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store
OpenWAF-Mitigated: challenge
X-Robots-Tag: noindex

{"error":"challenge_required","message":"www.example.com is under attack protection. Open it in a browser to continue."}
```

Si no, es una línea de texto plano, y un `HEAD` no lleva cuerpo. Tus clientes pueden reconocerlo por la cabecera `OpenWAF-Mitigated: challenge`.

- **Tu propia web.** Las llamadas que hacen páginas que ya pasaron el reto llevan la cookie y pasan. Si `www.example.com` llama a `api.example.com`, pon [`pass_scope: zone`](https://developers.openwaf.dev/es/waf/browser-challenge/#passes) para que un pase cubra los dos nombres, y haz las llamadas con credenciales.
- **Apps móviles, socios y clientes entre servidores** no pueden tener pase. Deja fuera sus nombres con [`hosts`](#hosts), exceptúa sus rutas o permite sus direcciones con una regla `allow` de IP o de ASN.
- **Webhooks** de pasarelas de pago y otros servicios: [exceptúa sus rutas](#exempt-webhooks).

`GET /__waf/status` responde `"required": true` en un nombre cubierto, así que una aplicación de una sola página puede recargar una vez para obtener el pase, igual que con la [barrera](https://developers.openwaf.dev/es/waf/browser-challenge/#spa).

## Qué pasa sin reto

Estas peticiones se saltan el modo bajo ataque (el resto del borde, como el WAF y los límites de peticiones, se les sigue aplicando):

| Petición | Notas |
|---|---|
| Las rutas propias del borde | `/__waf/…` (reto, estado, puerta de acceso) y `/.well-known/acme-challenge/…`. |
| Rutas de `exempt_paths` | Prefijos que coinciden por segmentos completos de ruta, sin distinguir mayúsculas: `/webhooks/stripe` coincide con `/webhooks/stripe/events`, no con `/webhooks/stripe2`. |
| Un `__waf_pass` válido | Un pase de la página del reto, obtenido antes o durante el modo. |
| [Reglas `allow` de IP](https://developers.openwaf.dev/es/waf/ip-and-country-rules/) | Las de la zona y las del nombre. Las reglas `allow` de país no eximen. |
| [Reglas `allow` de ASN](https://developers.openwaf.dev/es/waf/ip-and-country-rules/#asn-rules) | Todas las direcciones de la red permitida. |
| [Excepciones del WAF](https://developers.openwaf.dev/es/waf/waf-exceptions/) con `rule_id` `0` | Una excepción de todas las reglas que coincide con el host y la ruta. |
| Rastreadores verificados | Buscadores y otros rastreadores verificados por sus rangos de IP publicados, DNS inverso confirmado o Web Bot Auth, sea cual sea el modo del [control de rastreadores IA](https://developers.openwaf.dev/es/ai-crawl-control/verification/), así que los buscadores siguen indexando tu sitio. La política del control de rastreadores IA se les sigue aplicando. Una petición que solo dice ser un rastreador en su `User-Agent` recibe el reto. |

## Exceptuar webhooks

Los webhooks, las notificaciones de pago y los chequeos de salud no pueden resolver un reto. Añade sus prefijos de ruta a `exempt_paths` (hasta 50; una lista nueva sustituye la lista entera) al activar el modo o después:

```json
{"challenge": {"under_attack": {"exempt_paths": ["/webhooks/stripe", "/api/payments/callback", "/healthz"]}}}
```

Las rutas exceptuadas siguen pasando por el WAF, los límites de peticiones y tus reglas. Si un proveedor publica las direcciones desde las que envía, también sirve una regla `allow` de IP para ellas.

## Solo algunos nombres

`hosts` limita el modo a algunos nombres de la zona (hasta 50, el ápex o nombres bajo él); una lista vacía cubre todos los nombres con proxy. Por ejemplo, protege `www.example.com` y `shop.example.com` y deja `api.example.com`, que usa tu app móvil, como está:

```json
{"challenge": {"under_attack": {"enabled": true, "hosts": ["www.example.com", "shop.example.com"]}}}
```

El modo es un ajuste de la zona: también cubre los nombres que tienen sus propios ajustes `challenge`, y no se puede fijar por nombre con `hosts.<nombre>.challenge`.

## Dificultad y duración del pase

- `difficulty`: `0` (por defecto) usa la dificultad del reto del nombre más un bit, hasta 24, así que los atacantes pagan el doble del trabajo habitual. Pon 10–24 para un valor fijo; cada bit más duplica el trabajo, también para tus visitantes.
- `pass_ttl_minutes`: `0` (por defecto) mantiene la duración del pase del nombre. Pon 5–10080 para dar pases más cortos, por ejemplo `60`, de modo que un reto resuelto no se pueda reutilizar mucho tiempo. Se aplica a los pases obtenidos mientras el modo está activado.

## Fin automático

`until` es la hora (RFC 3339, UTC) a la que el modo se desactiva solo: más de un minuto por delante y como mucho 30 días. `null` lo mantiene activado hasta que lo desactives.

Cuando pasa `until`, en unos 30 segundos el plano de control desactiva el modo como un cambio normal: crea una versión nueva de la zona con el motivo «Under attack mode expired (until 2026-10-28T18:00:00Z)», que ves en el [historial de la zona](https://developers.openwaf.dev/es/change-control/) y en el registro de auditoría, y los nodos de borde la aplican como cualquier otra versión. Desactivar el modo a mano borra `until`.

## Activarlo y desactivarlo

Cada cambio se simula primero, necesita un motivo, crea una versión nueva y se puede revertir.

**Panel**

Dominio → resumen, o **Seguridad → Verificación** → **Modo bajo ataque**. Elige cuánto tiempo (1 h, 6 h, 24 h o hasta desactivarlo) y, en las opciones avanzadas, los nombres, las rutas exceptuadas, la dificultad y la duración del pase. **Pedir verificación (casilla)** viene activado; desactívalo para usar solo la prueba de trabajo. Revisa el cambio y aplícalo con un motivo. Mientras está activado, cada página de la zona muestra un aviso con el tiempo que queda y un botón para desactivarlo.
**CLI**

```sh
openwaf-cloud ctl zones under-attack example.com on --for 6h --exempt /webhooks/stripe --reason "Avalancha HTTP"
openwaf-cloud ctl zones under-attack example.com status
openwaf-cloud ctl zones under-attack example.com off --reason "Fin del ataque"
```
`on` y `off` muestran el cambio y piden confirmación (`--yes` la omite). `on` admite también `--hosts www.example.com,shop.example.com`, `--difficulty 18`, `--pass-ttl 60` e `--interactive=false` (prueba de trabajo sin la caja de verificación; sin la opción, la zona conserva lo que tenía); sin `--for` queda activado hasta que lo desactives. `status` indica si se pide el clic. También sirve `zones set example.com challenge.under_attack.interactive=false …`. Consulta [`zones under-attack`](https://developers.openwaf.dev/es/cli/reference/zones/#zones-under-attack).
**API**

```sh
curl -s -X PATCH "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/edge?dry_run=1" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Change-Reason: Avalancha HTTP" \
  -d '{"challenge": {"under_attack": {"enabled": true, "until": "2026-10-28T18:00:00Z", "exempt_paths": ["/webhooks/stripe"]}}}'
```
`?dry_run=1` muestra el cambio sin aplicarlo; envía la misma petición sin él para aplicarlo. Añade `"interactive": false` dentro de `under_attack` para usar la prueba de trabajo sin la caja de verificación. Para desactivar el modo, envía `{"challenge": {"under_attack": {"enabled": false}}}`. En `GET …/edge`, `challenge.under_attack` no aparece mientras el modo no se ha configurado nunca o ha vuelto a sus valores por defecto; cuando aparece, muestra `interactive` como `true` o `false`.
**MCP**

`under_attack` con `{"zone": "example.com", "enabled": true, "duration_minutes": 360, "exempt_paths": ["/webhooks/stripe"], "reason": "Avalancha HTTP"}`, primero `confirm=false` y después `confirm=true` con `expected_version`. `duration_minutes` va de 1 a 43200; sin él, el modo sigue activado hasta que lo desactives. También admite `hosts`, `difficulty`, `pass_ttl_minutes` e `interactive` (`false` para la prueba de trabajo sola). Para desactivarlo: `{"zone": "example.com", "enabled": false, "reason": "Fin del ataque"}`. `update_settings` también acepta `challenge.under_attack`.


## Campos

`challenge.under_attack` en los ajustes de borde de la zona:

| Campo | Valores | Por defecto | Significado |
|---|---|---|---|
| `enabled` | `true`, `false` | `false` | Activa el modo. `false` borra `until`. |
| `until` | Hora RFC 3339 UTC, o `null` | `null` | Cuándo el modo [se desactiva solo](#until): de 1 minuto a 30 días por delante. Solo con `enabled: true`; `null` significa hasta que lo desactives. |
| `hosts` | 0–50 nombres de la zona | `[]` | Nombres que cubre; vacío significa todos los nombres con proxy. |
| `exempt_paths` | 0–50 prefijos de ruta | `[]` | Rutas que nunca reciben el reto, como los webhooks. |
| `difficulty` | `0`, o 10–24 | `0` | `0`: la dificultad del reto del nombre más uno, hasta 24. |
| `pass_ttl_minutes` | `0`, o 5–10080 | `0` | Duración de los pases obtenidos con el modo activado. `0`: el `pass_ttl_minutes` del nombre. |
| `interactive` | `true`, `false` | `true` | Pide un clic en la [caja de verificación](#interactive-step) tras la prueba de trabajo. `false`: la página pasa sola. `null` vuelve a `true`; activar el modo sin indicarlo conserva el valor actual. |

Los campos se combinan uno a uno como el resto de ajustes; `hosts` y `exempt_paths` sustituyen la lista entera, un campo a `null` vuelve a su valor por defecto y `"under_attack": null` desactiva el modo y restablece todos los campos. `hosts.<nombre>.challenge.under_attack` se rechaza con `422`: usa `challenge.under_attack.hosts`.

## Seguir el ataque

Dominio → **Seguridad → Verificación** muestra si el modo está activado, hasta cuándo y si pide clic, y los retos que emitió, que se resolvieron y que fallaron bajo el disparador `under_attack`, junto al resto de la [analítica de retos](https://developers.openwaf.dev/es/waf/browser-challenge/#analytics). Las resoluciones con la caja de verificación se cuentan además como interactivas. Una petición que dice falsamente ser un rastreador se cuenta en `crawler`. El mismo informe está en `openwaf-cloud ctl challenge report example.com`, `GET /v1/orgs/{org}/zones/{zone}/challenge/analytics` y la herramienta MCP `challenge_report`.

Los retos del modo bajo ataque también reciben [puntuación de riesgo](https://developers.openwaf.dev/es/waf/verify-risk/), como cualquier otro: la sección de riesgo del informe muestra su reparto por nivel. En el modo enforce de la puntuación, una puntuación alta puede endurecer la prueba de trabajo o bloquear el reto; la caja de verificación en sí no depende de la puntuación.

Una tasa de resolución alta indica que son sobre todo visitantes reales; muchos retos emitidos que nunca se resuelven, o muchos fallos en el paso interactivo, son el ataque detenido en el borde.
