Si estás leyendo esto, probablemente ya viste un 429 Too Many Requests con un header x-kpsdk-ct en la respuesta y te preguntaste qué acaba de decidir que tu cliente HTTP no es humano. Kasada Anti-Bot explicado significa, en la práctica, entender una pila de defensa que combina fingerprinting TLS, reputación de IP, una máquina virtual de bytecode ofuscada y tokens firmados que rotan por sesión. En este artículo desglosamos cada capa y mostramos un enfoque legítimo para que ips.js se ejecute en un navegador real y emita un KP_UIDz válido, usando salidas residenciales de ProxyHat.
El contexto es importante: Kasada no es un WAF clásico ni un simple reto de JavaScript. Es un sistema de prueba de trabajo basado en una VM personalizada de aproximadamente 449 KB que ejecuta bytecode propio, con una tabla de strings codificada, semillas temporales y checksums de integridad. El cliente debe resolver el reto, devolver una prueba criptográfica y mantenerla fresca. Cualquier intento de reimplementar la VM en Python puro o Node.js sin un runtime de navegador real tiende a romperse en días, porque Kasada rota el script y los offsets con frecuencia.
Arquitectura de Kasada Anti-Bot: ips.js, KP_UIDz y la familia x-kpsdk
Cuando un navegador carga una página protegida por Kasada, el servidor inyecta o redirige a un script de reto típicamente servido desde un path como /ips.js o un equivalente ofuscado. Este script no es JavaScript legible: es un cargador que inicializa una máquina virtual de bytecode personalizada, decodifica una tabla de strings (XOR + desplazamientos dependientes de una semilla temporal), y ejecuta una serie de rutinas que recolectan señales del entorno del navegador.
El flujo simplificado es:
- El cliente solicita la página; el edge de Kasada evalúa primero señales pasivas (TLS, HTTP/2, IP).
- Si las señales pasivas son aceptables, se sirve
ips.jsy el navegador lo ejecuta. - La VM recolecta un fingerprint de navegador+dispositivo, realiza una prueba de trabajo y emite un payload cifrado.
- El payload se envía al endpoint de Kasada, que valida y emite/renueva la cookie
KP_UIDz. - Las solicitudes subsiguientes llevan los headers
x-kpsdk-ct,x-kpsdk-cdyx-kpsdk-dv, que Kasada verifica en cada request.
El header x-kpsdk-ct es el token de control principal: contiene un blob cifrado y firmado que encapsula el resultado del reto y metadatos de sesión. Si Kasada devuelve un 429 y la respuesta incluye x-kpsdk-ct, significa que el token fue rechazado —expiró, fue manipulado, o la firma no coincide. x-kpsdk-cd suele transportar datos de contexto (navegador, viewport, timestamps) y x-kpsdk-dv datos derivados del dispositivo. Los tres rotan: reutilizar un token capturado estáticamente solo funciona durante una ventana corta.
La VM de bytecode: por qué reimplementarla es frágil
La VM de ips.js implementa un conjunto de opcodes propios, con un intérprete que despacha instrucciones desde un array de bytes. La tabla de strings está codificada y se descodifica en runtime usando una semilla que depende del timestamp del servidor y de valores derivados del DOM. Además, hay checksums de integridad: si el intérprete detecta que el bytecode fue modificado (por ejemplo, por un hook de Function.prototype.toString), aborta o emite un payload inválido deliberadamente.
Esto explica por qué los enfoques de kasada bypass basados en parchear el script o en reimplementar la VM en Python tienen una vida útil corta. Cada vez que Kasada rota offsets, opcodes o la semilla, el parche deja de aplicar. La estrategia robusta no es reimplementar la VM, sino dejar que se ejecute en un navegador real y controlar el entorno que observa.
Recolección de fingerprint y emisión de payloads cifrados
Dentro de la VM, Kasada recolecta señales que cubren varias dimensiones:
- Señales de navegador:
navigator.userAgent,navigator.platform,navigator.webdriver,navigator.languages,navigator.hardwareConcurrency,screen.width/height,devicePixelRatio, zona horaria víaIntl.DateTimeFormat().resolvedOptions().timeZone. - Señales de canvas: renderiza texto y geometría en un
<canvas>y aplica un hash al resultado. Diferencias en GPU, drivers y anti-aliasing producen hashes distintos. - Señales de WebGL: vendor y renderer de
WEBGL_debug_renderer_info, parámetros de extensión. - Señales de comportamiento: timings entre eventos, presencia de
mousemove,keydown, secuencia de foco. - Señales de entorno: presencia de
window.callPhantom,__nightmare,cdc_propiedades de Chrome DevTools Protocol, hooks enevaloFunction.
El payload resultante se cifra (típicamente AES con una clave derivada de la semilla temporal) y se envía al endpoint de Kasada. El servidor valida el payload, verifica la prueba de trabajo y emite el KP_UIDz. A partir de ahí, cada request debe llevar los headers x-kpsdk-* actualizados. Si el payload no coincide con el fingerprint esperado para esa IP y sesión, o si los timings son imposibles (por ejemplo, resolver el reto en 2 ms), Kasada marca la sesión como sospechosa.
Fingerprinting TLS (JA3/JA4) y HTTP/2 antes del reto JS
Una capa que se subestima: Kasada evalúa el cliente antes de servir el reto. Esto significa que tu stack TLS y HTTP/2 ya está siendo juzgado cuando todavía no se ha descargado ips.js. Kasada usa huellas tipo JA3 y el más reciente JA4 para comparar el orden de cipher suites, la lista de extensiones y el orden de curvas elípticas contra perfiles conocidos de navegadores reales.
Por ejemplo, un cliente Python con requests + urllib3 presenta un ClientHello con un orden de cipher suites y un conjunto de extensiones que no coincide con ningún Chrome o Firefox real. Lo mismo ocurre con muchos HTTP clients de Node.js que usan OpenSSL con configuración por defecto. Kasada mantiene un catálogo de huellas TLS válidas y, si la tuya no encaja, el reto JS puede nunca servirse o servirse en una variante diseñada para fallar.
En HTTP/2, Kasada revisa el SETTINGS frame inicial, el orden de pseudo-headers (:method, :authority, :scheme, :path), el WINDOW_UPDATE inicial y los valores de HEADER_TABLE_SIZE. Chrome, Firefox y Safari tienen perfiles distintos y consistentes. Un cliente que envíe :path antes que :method queda inmediatamente fuera de distribución.
El fingerprint TLS no es algo que un proxy resuelva por ti: el proxy termina la conexión TCP pero el TLS lo negocias tú. Por eso, para pasar limpio necesitas (a) un cliente cuyo stack TLS coincida con un navegador real —típicamente un navegador headless o con parches de stealth— y (b) una IP cuya reputación no levante sospechas antes de llegar al reto.
Por qué los proxies residenciales son esenciales para Kasada
Kasada realiza scoring de reputación de IP de forma agresiva. Los rangos de datacenter (AWS, GCP, Azure, OVH, Hetzner, DigitalOcean) están en listas que reciben un peso de confianza bajo o directo pre-block. No es que siempre devuelvan 403 —a veces Kasada los deja pasar al reto pero con un umbral de validación mucho más alto, de modo que cualquier desviación mínima del fingerprint provoca bloqueo.
Los proxies residenciales ofrecen IPs registradas a ISPs de consumo (Comcast, AT&T, Movistar, Orange, etc.), que Kasada trata con confianza por defecto porque históricamente provienen de tráfico humano real. La diferencia práctica es enorme: con una IP de datacenter puedes ver tasas de éxito del 15–30% incluso con un navegador perfecto, mientras que con una IP residencial limpia y un navegador real puedes sostener 90%+ en rutas moderadas.
Los proxies móviles (4G/5G) son aún más confiables porque comparten CGNAT con miles de usuarios legítimos, pero son más caros y lentos. Para la mayoría de casos de automatización autorizada, los residenciales son el punto dulce entre confianza, costo y latencia.
| Tipo de proxy | Confianza de Kasada | Latencia típica | Costo relativo | Caso recomendado |
|---|---|---|---|---|
| Datacenter | Baja — pre-block frecuente | 30–80 ms | Bajo | No recomendado para Kasada |
| Residencial | Alta | 150–600 ms | Medio | Automatización autorizada, scraping de datos públicos |
| Móvil (4G/5G) | Muy alta | 400–1200 ms | Alto | Cuentas sensibles, rutas de alto valor |
Enfoque legítimo: navegador real + proxies residenciales ProxyHat
La estrategia que funciona de forma sostenible para automatización autorizada —investigación de seguridad, pentesting con autorización, monitoreo de datos públicos— es no reimplementar la VM, sino dejar que ips.js se ejecute en un navegador real y controlar el entorno que observa. Esto significa:
- Usar un navegador basado en Chromium o Firefox con stealth patches (ocultar
navigator.webdriver, limpiarcdc_propiedades, parchearFunction.prototype.toString). - Asegurar que el stack TLS y HTTP/2 del navegador coincide con un perfil real (un Chrome sin modificar ya lo hace).
- Enrutar el tráfico del navegador a través de un proxy residencial para que la IP tenga buena reputación.
- Rotar sesiones con sticky sessions de ProxyHat cuando sea apropiado, manteniendo
KP_UIDzconsistente durante la vida de la sesión.
A continuación, un ejemplo con Playwright en Python, usando SOCKS5 de ProxyHat para enrutar el navegador:
from playwright.sync_api import sync_playwright
PROXY = {
"server": "socks5://gate.proxyhat.com:1080",
"username": "user-country-US-session-sess01",
"password": "PASSWORD"
}
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False,
proxy=PROXY,
args=[
"--disable-blink-features=AutomationControlled",
"--disable-features=IsolateOrigins,site-per-process"
]
)
context = browser.new_context(
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36"
),
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone_id="America/New_York"
)
page = context.new_page()
page.goto("https://example-protected.com/")
page.wait_for_timeout(8000) # deja que ips.js resuelva el reto
cookies = context.cookies()
print([c for c in cookies if c["name"] == "KP_UIDz"])
browser.close()
Con HTTP estándar (por ejemplo, para reutilizar la cookie en un cliente HTTP más rápido una vez resuelto el reto):
curl -x http://user-country-US-session-sess01:PASSWORD@gate.proxyhat.com:8080 \
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" \
-H "accept-language: en-US,en;q=0.9" \
-b "KP_UIDz=VALOR_OBTENIDO_DEL_NAVEGADOR" \
https://example-protected.com/api/data
La clave es que el KP_UIDz y los headers x-kpsdk-ct se obtienen del navegador real y se reinyectan en el cliente HTTP. Esto solo es estable si mantienes la misma IP (sticky session) y la ventana de validez del token no expira —típicamente minutos, no horas.
Para sesiones sticky en ProxyHat, usa el flag session- en el username:
http://user-country-US-session-abc123:PASSWORD@gate.proxyhat.com:8080
Para geo-targeting a ciudad:
http://user-country-DE-city-berlin-session-abc123:PASSWORD@gate.proxyhat.com:8080
Consulta las documentaciones oficiales de ProxyHat para detalles de rotación y límites de concurrencia, y revisa la lista de ubicaciones para elegir salidas alineadas con la zona horaria y locale que declaras en el navegador.
Rotación de sesiones y manejo del 429
Cuando recibes un 429 con x-kpsdk-ct en la respuesta, la acción correcta no es reintentar inmediatamente con la misma sesión: el token ya está marcado. Lo que funciona es:
- Descartar la sesión actual (cookie + headers).
- Rotar a una nueva IP residencial con un nuevo
session-. - Volver a resolver
ips.jsen el navegador real con la nueva IP. - Reanudar las solicitudes con el nuevo
KP_UIDz.
Implementar backoff exponencial con jitter (por ejemplo, base 2 s, factor 2, jitter ±20%) reduce la presión y evita patrones detectables. Mantén concurrencia moderada —5–20 sesiones simultáneas por objetivo es un rango razonable para monitoreo de datos públicos.
Errores comunes y casos límite
1. Reutilizar tokens estáticos
Capturar x-kpsdk-ct una vez y reinyectarlo en mil requests es el error más común. Los tokens rotan y Kasada detecta reuso por contador de request y ventana temporal. Si ves un 429 tras pocas solicitudes con un token recién capturado, casi siempre es reuso detectado.
2. Mismatch entre IP y locale/timezone
Si tu navegador declara timezone_id="America/New_York" pero tu IP residencial está en Frankfurt, Kasada lo marca como inconsistencia. Asegúrate de que la ubicación del proxy coincida con el locale y timezone del navegador.
3. Headless puro sin stealth
Chrome headless sin parches expone navigator.webdriver=true y varias señales de CDP. Kasada las detecta. Usa --disable-blink-features=AutomationControlled como mínimo y considera herramientas como Playwright con contextos stealth o navegadores parcheados.
4. Confiar en que el proxy arregla el TLS
El proxy no reesnegocia tu TLS. Si usas requests detrás de un proxy residencial, tu ClientHello sigue siendo el de urllib3. Necesitas un cliente cuyo TLS coincida con un navegador real, o un navegador real directamente.
Uso apropiado y consideraciones legales
Este enfoque es para automatización autorizada: investigación de seguridad con permiso del propietario del sistema, pentesting dentro del alcance acordado, monitoreo de datos públicamente accesibles respetando robots.txt y términos de servicio, y casos de uso legítimos de recopilación de datos públicos. No es para fraude, evasión de límites de pago, creación masiva de cuentas falsas ni ninguna actividad que viole la ley.
En EE. UU., la Computer Fraud and Abuse Act (CFAA) penaliza el acceso no autorizado a sistemas protegidos. En la UE, el GDPR regula el tratamiento de datos personales, y scrapeos que involucren datos personales requieren base legal. Antes de automatizar contra cualquier sitio, confirma que tienes autorización o que el dato es público y no está protegido por ToS aplicables. Cuando haya duda, consulta con asesoría legal.
ProxyHat no promueve el kasada bypass como evasión de protecciones legítimas: el objetivo es permitir automatización legítima —investigación, QA, monitoreo de precios públicos— en igualdad de condiciones técnicas con un usuario humano. Revisa la página de precios para planes residenciales y los casos de uso de web scraping y seguimiento de SERP para ejemplos de implementación responsable.
Puntos clave (Key Takeaways)
Kasada combina fingerprinting TLS/HTTP/2, reputación de IP y una VM de bytecode de ~449 KB en
ips.js. Elx-kpsdk-ctes el token de control que rota por sesión; un 429 con ese header significa token inválido. Los proxies residenciales son esenciales porque Kasada pre-bloquea ASNs de datacenter. La estrategia sostenible para automatización autorizada es ejecutarips.jsen un navegador real con stealth, enrutarlo por IPs residenciales de ProxyHat y reutilizar elKP_UIDzdentro de su ventana de validez.
- No reimplementes la VM: deja que
ips.jscorra en un navegador real. - El TLS se evalúa antes del reto: usa un navegador cuyo ClientHello coincida con un perfil real.
- La IP importa desde el primer byte: residenciales > móviles > datacenter para Kasada.
- Consistencia IP/locale/timezone: un mismatch es una señal tan fuerte como
navigator.webdriver. - Rotación con sticky sessions: mantén
KP_UIDzconsistente durante la vida de la sesión. - Uso legítimo únicamente: investigación autorizada y datos públicos, nunca fraude.
Para escalar esto en producción, combina sesiones sticky de ProxyHat con un pool de navegadores reales (no headless puro), backoff exponencial con jitter y rotación de KP_UIDz por sesión. Si necesitas cobertura geográfica específica, revisa las ubicaciones disponibles y alinea cada sesión con su país y ciudad correspondientes.






