CrafyCAPTCHA Documentación
Bienvenido a la documentación oficial de CrafyCAPTCHA, una plataforma avanzada y modular de protección contra bots, ataques DDoS de tráfico masivo y spam.
A diferencia de los captchas tradicionales, CrafyCAPTCHA emplea un modelo de fricción adaptativa. Los usuarios legítimos no ven nada o solo un check, mientras que el tráfico sospechoso enfrenta retos biométricos y criptográficos escalonadas.
Propuesta de Valor
- Invisible cuando es seguro, riguroso cuando es dudoso: Pruebas invisibles basadas en Proof-of-Work para tráfico seguro.
- Personalización Extrema (White-label): El widget se adapta al diseño de tu marca (colores, bordes, tipografía).
- Fácil Integración (Drop-in): SDKs limpios para Frontend y Backend listos para producción.
Primeros Pasos
Comenzar a proteger tu sitio con CrafyCAPTCHA es rápido y sencillo. Sigue estos pasos para tu primera integración:
- Crear una cuenta: Necesitarás acceder con una cuenta de Crafy Account. Puedes usar la que ya tienes o crear una nueva si aún no la posees. Inicia sesión en nuestro Panel de Control.
-
Añadir Dominios: En el panel, dirígete a la sección Mis Sitios y añade los
dominios que deseas proteger.
Nota: El dominiolocalhostviene autorizado por defecto para que puedas probar tu integración localmente sin problemas. -
Crear una Credencial: En la sección Credenciales, haz clic en
Nueva Credencial. Rellena la información solicitada:
- Nombre (Etiqueta): Un identificador para tu llave (ej. Producción o Desarrollo).
- Plan: Elige el plan que mejor se adapte a tu volumen de tráfico.
-
Guardar tu Secret Key: Al crear la credencial, se te proporcionarán las claves necesarias para tu integración. Importante: la
Secret Keysolo se te mostrará una vez. Guárdala en un lugar seguro (como un archivo.env). - Iniciar Integración: Una vez tengas tus credenciales, estarás listo para comenzar. Dirígete a la sección de El Flujo de Integración para conectar tu Backend y Frontend.
Arquitectura del Sistema
El sistema se divide en capas que analizan y mitigan intentos de acceso malicioso:
1. El Motor de Desafíos (IA y Análisis de Riesgo)
Evalúa una matriz de riesgo sobre el usuario para determinar un Risk Score (entre
0.0 y 1.0). Analiza la reputación de la IP, geolocalización, uso de Tor o
DataCenters, y comportamiento de Headers/User-Agents.
2. Validaciones Multi-Capa
- Prueba de Trabajo (PoW): Usa Altcha y Cloudflare Turnstile en background. Si el riesgo aumenta, la dificultad computacional sube.
- Telemetría y Huella Digital del Navegador: Análisis profundo del entorno del cliente, Private Access Tokens y Aprendizaje Automático para detectar anomalías.
- Retos Dinámicos: Pueden ser totalmente invisibles, checkbox, slider o connect (en ese orden según el Risk Score detectado).
Credenciales
Para integrar CrafyCAPTCHA necesitas 4 credenciales. Las 3 primeras (Public Key,
Secret Key y Signing Public Key) se obtienen desde tu
Panel de Control. El
Public Token se obtiene dinámicamente desde el SDK Backend mediante el método
getPublicToken().
| Credencial | Uso | Exposición | ¿Cambia? |
|---|---|---|---|
Public Key |
Identifica tu cuenta. Se usa tanto en el Backend (SDKs) como en el Frontend (SDK Client-Side). | Client-Side + Server-Side | Nunca |
Secret Key |
Clave secreta para operaciones criptográficas del servidor (crear flows, verificar tokens). Solo se muestra una vez al crear la credencial. | Solo Server-Side | Nunca |
Signing Public Key |
Clave pública criptográfica usada por el SDK Client-Side para verificar firmas del iframe. | Client-Side + Server-Side | Nunca |
Public Token |
Token público que identifica el plan activo de tu credencial. Se envía al iframe como parámetro. | Client-Side + Server-Side | Sí (al cambiar de plan) |
La Secret Key solo se muestra una vez en el momento de crear la
credencial. Si la pierdes, deberás generar una nueva credencial. Por seguridad, nunca la
expongas en código del lado del cliente (HTML, JavaScript público, repositorios, etc.).
Cuando tu credencial cambia de plan (upgrade o downgrade), el Public Token se regenera
automáticamente. Los SDKs Backend gestionan este cambio de forma dinámica: utiliza el método
getPublicToken() para obtener siempre el valor actualizado y pasárselo al SDK
Client-Side. Las demás credenciales permanecen iguales.
Buenas prácticas: almacenamiento seguro
Se recomienda centralizar todas las credenciales en un archivo de variables de entorno, fuera del código fuente y del control de versiones.
# .env (PHP con phpdotenv, Node.js con dotenv, Python con python-dotenv)
CRAFY_PUBLIC_KEY=pk_xxxxxxxxxxxxxxxx
CRAFY_SECRET_KEY=sk_xxxxxxxxxxxxxxxx
CRAFY_SIGNING_PUBLIC_KEY=base64_xxxxxxxx
Nota: el Public Token no se almacena en el .env porque se obtiene
dinámicamente desde el SDK Backend mediante el método getPublicToken().
Luego, en tu código, carga las variables desde el archivo .env:
// PHP (con phpdotenv)
$crafy = new CrafyCAPTCHA(
$_ENV['CRAFY_PUBLIC_KEY'],
$_ENV['CRAFY_SECRET_KEY']
);
// Node.js (con dotenv)
require('dotenv').config();
const captcha = new CrafyCAPTCHA(
process.env.CRAFY_PUBLIC_KEY,
process.env.CRAFY_SECRET_KEY
);
# Python (con python-dotenv)
import os
from dotenv import load_dotenv
from crafy_captcha import CrafyCAPTCHA
load_dotenv()
captcha = CrafyCAPTCHA(
os.environ['CRAFY_PUBLIC_KEY'],
os.environ['CRAFY_SECRET_KEY']
)
Añade .env a tu archivo .gitignore para evitar exponer tus credenciales
en sistemas de control de versiones. Incluye un archivo .env.example con las claves
vacías como referencia para otros desarrolladores.
El Flujo de Integración
Para proteger un formulario (ej. un Login), el flujo requiere la participación coordinada de tu servidor y del navegador del usuario.
- Crear el Flujo (Backend): Usando el SDK de Backend en tu servidor, generas el
encryptedIframeOptions(que contiene el Nonce y la configuración del desafío firmados criptográficamente) a través del métodocreateFlow. - Renderizado (Frontend): Pasas el
encryptedIframeOptionsgenerado por el servidor al SDK Client-Side (ej. JavaScript o React). Este SDK inyecta de forma segura el iframe de CrafyCAPTCHA, donde el usuario resuelve el reto (ya sea un puzzle visual o una validación transparente e invisible). - Aprobación y Envío (Frontend): Una vez completado el reto con éxito, el Iframe
emite un Token Cifrado vía
postMessageal SDK Client-Side. Este token se coloca automáticamente en un input oculto y se envía al servidor cuando el usuario hace submit del formulario. - Verificación Final (Backend): Al recibir los datos del formulario web, tu servidor
toma el Token y lo valida de forma local, atómica y segura utilizando el método
verifyFlowdel SDK Backend. Si el resultado es válido (true), el acceso es concedido.
Frontend SDKs
Integra CrafyCAPTCHA en el frontend utilizando nuestras librerías oficiales. Estas se encargan de inyectar el iframe, aplicar estilos y comunicarse de forma asíncrona segura.
SDKs Disponibles del lado del cliente
1. JavaScript Vanilla
Puedes usar el CDN de jsDelivr para incluir la versión Vanilla JS:
GitHub: crafy-captcha-js
Stats y versiones: jsDelivr
Package
Paquete: npmjs
Package
A. Inclusión de la Librería
<script src="https://captcha.crafy.net/cdn-js/1.1.7.js" integrity="sha384-ij1DhfbKim6bT2zXcO46f/BJU4ucV6v0ajMv9NqZ/j2qi99huo54M8A4EhChcxnP" crossorigin="anonymous"></script>
Puedes forzar a la última versión usando
https://captcha.crafy.net/cdn-js/last.js (no
recomendado para producción).
B. Inicializar el Widget
Coloca un contenedor <div> en tu HTML (ej.
<div id="crafy-container"></div>) y llama a CrafyCAPTCHA.init():
document.addEventListener('DOMContentLoaded', () => {
CrafyCAPTCHA.init(
'crafy-container', // ID del div contenedor
'TU_PUBLIC_KEY', // Public Key (Credencial)
'TU_PUBLIC_TOKEN', // Public Token (Credencial)
'TU_SIGNING_PUBLIC_KEY', // Signing Public Key (Base64)
{
// Opciones de configuración de seguridad (es obligatorio usar UNA de estas dos)
optionsUrl: '/crafy-options.php', // (RECOMENDADO: obtiene las opciones mediante Fetch para evitar problemas con caché)
// encryptedOptions: 'TU_ENCRYPTED_IFRAME_OPTIONS', // (Alternativa: opciones inyectadas directamente por PHP en el HTML)
// Opciones de apariencia y eventos (estas no se cifran)
iframeUrl: 'https://captcha.crafy.net/challenge', // URL base del challenge
inputName: 'CrafyCAPTCHA_token', // Nombre del input hidden final
theme: 'dark', // 'light' o 'dark'
onSuccess: (token) => {
console.log("Humano verificado. Token:", token);
}
}
);
});
C. Carga Diferida (defer / async)
Para optimizar el rendimiento de tu página, puedes cargar la librería de forma diferida usando el
atributo
defer o async en el tag <script>. Esto evita que el script
bloquee el renderizado de la página.
<script src="https://captcha.crafy.net/cdn-js/1.1.7.js" integrity="sha384-ij1DhfbKim6bT2zXcO46f/BJU4ucV6v0ajMv9NqZ/j2qi99huo54M8A4EhChcxnP" crossorigin="anonymous" defer></script>
Al usar defer o async, la clase CrafyCAPTCHA podría no estar
disponible todavía cuando se ejecuta DOMContentLoaded. Para estos casos, la librería
dispara automáticamente un evento personalizado CrafyCAPTCHALoaded en window
cuando ha terminado de cargar. Usa este evento para inicializar el widget de forma segura:
<!-- 1. Registrar el listener ANTES de cargar el script -->
<script>
window.addEventListener('CrafyCAPTCHALoaded', () => {
CrafyCAPTCHA.init(
'crafy-container',
'TU_PUBLIC_KEY',
'TU_PUBLIC_TOKEN',
'TU_SIGNING_PUBLIC_KEY',
{
optionsUrl: '/crafy-options.php',
inputName: 'CrafyCAPTCHA_token',
onSuccess: (token) => {
console.log("Humano verificado. Token:", token);
}
}
);
});
</script>
<!-- 2. Cargar la librería con defer -->
<script src="https://captcha.crafy.net/cdn-js/1.1.7.js" integrity="sha384-ij1DhfbKim6bT2zXcO46f/BJU4ucV6v0ajMv9NqZ/j2qi99huo54M8A4EhChcxnP" crossorigin="anonymous" defer></script>
defer: El script se descarga en paralelo y se ejecuta después de que el DOM esté listo, respetando el orden de los scripts. Es la opción recomendada.async: El script se descarga en paralelo y se ejecuta tan pronto como se descarga, sin esperar al DOM ni a otros scripts. Útil si no hay dependencias de orden, pero requiere usar el eventoCrafyCAPTCHALoadedsí o sí.
En ambos casos, registrar el listener de CrafyCAPTCHALoaded antes del
tag <script> que carga la librería garantiza que nunca perderás el evento.
D. Opciones de Inicialización
El quinto parámetro del método init() es un objeto de configuración para
personalizar la carga de seguridad, el comportamiento y la apariencia del widget en el lado del cliente:
| Propiedad | Tipo | Descripción |
|---|---|---|
optionsUrl |
String | (Recomendado) Ruta del endpoint en tu servidor que devuelve dinámicamente
las opciones. El SDK JavaScript hará una petición POST interna para obtener los datos. Tu
backend debe procesar la solicitud llamando al método createFlow() del SDK y
retornar un JSON con la propiedad eo:
echo json_encode(['eo' => $crafy->createFlow()]);. Ideal si tu web utiliza
sistemas de caché (como Cloudflare o WP Rocket) ya que evita conflictos de nonces.
|
encryptedOptions |
String | (Alternativa Legacy) El string encriptado que genera el servidor usando
createFlow(), inyectado directamente por PHP (o NodeJS) en el código HTML de la
página. Nota: Si la página es cacheada, el captcha fallará por Replay Attack. Es
obligatorio usar optionsUrl o encryptedOptions.
|
theme |
String | Tema predefinido del widget. Puede ser 'light' o 'dark'. Si no se
especifica, se aplicará 'light'. |
inputName |
String | Nombre del campo <input type="hidden"> que el widget creará e inyectará
en el formulario automáticamente cuando el reto se resuelva. Por defecto es
'CrafyCAPTCHA_token'.
|
onSuccess |
Function | Función callback que se ejecuta tras validar exitosamente el captcha. Recibe como argumento
el token final (en formato base64) que debe enviarse al backend. |
iframeUrl |
String | (Opcional) Permite sobrescribir la ruta base donde está montado el endpoint de desafíos. |
style |
Object |
Objeto para una personalización total de los colores del widget. Las propiedades soportadas
son:
|
E. Control de Carga Automática (Opcional)
Por defecto, el widget intentará precargar el desafío automáticamente en segundo plano después de su inicialización para garantizar que la verificación sea lo más rápida posible para el cliente.
Puedes desactivar este comportamiento usando el método setAutoLoad(false)
antes de llamar a init(). Si la carga automática se desactiva, el iframe
del desafío no se cargará hasta que el usuario haga clic explícitamente en el checkbox de verificación.
document.addEventListener('DOMContentLoaded', () => {
// 1. Desactivar carga automática
CrafyCAPTCHA.setAutoLoad(false);
// 2. Inicializar
CrafyCAPTCHA.init('crafy-container', 'TU_PUBLIC_KEY', 'TU_PUBLIC_TOKEN', 'TU_SIGNING_PUBLIC_KEY', {
optionsUrl: '/crafy-options.php'
});
});
Para forzar la carga de forma manual o programática desde fuera del script (por ejemplo, cuando el usuario hace focus en un input de texto del formulario), puedes utilizar:
CrafyCAPTCHA.loadIframe();
setAutoLoad(true)(Por defecto): Mejora notablemente la experiencia del cliente obteniendo una verificación ligeramente más rápida. Sin embargo, cada carga consume cuota de la credencial (descuenta una llamada achallenge).setAutoLoad(false): Ahorra llamadas y consumo de la cuota de la credencial. Te recomendamos encarecidamente ponerlo en false cuando insertas el widget en una página pública donde muchos usuarios entran pero pocos envían el formulario, como por ejemplo un formulario de comentarios al final del artículo de un blog.
F. Múltiples Widgets en la misma página
Puedes renderizar múltiples instancias de CrafyCAPTCHA dentro de la misma página web simplemente llamando
a CrafyCAPTCHA.init() varias veces. Esto es extremadamente útil si tienes, por ejemplo, un
formulario de registro y otro de login visibles al mismo tiempo (ej. en un modal).
Para evitar colisiones entre widgets, debes asegurarte de cumplir estrictamente dos reglas:
- Cada llamada a
init()debe apuntar a un ID de contenedor único. - Cada widget debe configurar un
inputNameúnico en su objeto de opciones. Si dos widgets comparten el mismo nombre de input, los tokens se sobrescribirán y el SDK arrojará un error crítico en la consola.
// Inicializar Widget 1 (Login)
CrafyCAPTCHA.init('crafy-container-login', 'PK', 'PT', 'SPK', {
optionsUrl: '/crafy-options.php',
inputName: 'CrafyCAPTCHA_token_login'
});
// Inicializar Widget 2 (Registro)
CrafyCAPTCHA.init('crafy-container-register', 'PK', 'PT', 'SPK', {
optionsUrl: '/crafy-options.php',
inputName: 'CrafyCAPTCHA_token_register'
});
Si la arquitectura de tu sistema requiere colocar dos o más widgets distintos dentro del mismo formulario HTML, el SDK maneja la validación global de forma inteligente mediante "Cascading Submit".
Cuando el usuario presiona el botón de "Enviar" del formulario, el SDK detectará automáticamente todos los widgets no resueltos, e irá desplegando el desafío correspondiente de uno en uno, pasándose el turno dinámicamente. El formulario solo se enviará de forma efectiva al backend cuando el usuario haya resuelto satisfactoriamente la totalidad de los widgets anclados a dicho formulario.
2. React SDK
Si usas React, disponemos de un componente oficial que simplifica el flujo de datos.
NPM: @crafyholding/crafy-captcha-react
A. Instalación
npm install @crafyholding/crafy-captcha-react
B. Uso del Componente
import React, { useState } from 'react';
import CrafyCaptcha from '@crafyholding/crafy-captcha-react';
function FormularioLogin() {
const [captchaToken, setCaptchaToken] = useState(null);
const manejarEnvio = (e) => {
e.preventDefault();
if (!captchaToken) {
alert('Por favor, resuelve el CAPTCHA primero.');
return;
}
console.log("Enviando al backend...", captchaToken);
};
return (
<form onSubmit={manejarEnvio}>
<input type="email" placeholder="Tu correo" />
<CrafyCaptcha
publicKey="TU_PUBLIC_KEY"
publicToken="TU_PUBLIC_TOKEN"
signingPublicKey="TU_SIGNING_PUBLIC_KEY"
encryptedIframeOptions="TU_ENCRYPTED_IFRAME_OPTIONS"
options={{ theme: 'dark' }}
onSuccess={(token) => {
console.log("¡CAPTCHA Resuelto!");
setCaptchaToken(token);
}}
/>
<button type="submit">Entrar</button>
</form>
);
}
export default FormularioLogin;
Asegúrate de que el dominio donde usas estos scripts esté en tu lista de orígenes (Referers) autorizados, de lo contrario la API rechazará la petición.
Backend (SDKs & API)
El backend te permite crear flujos de protección y validar los payloads entrantes del cliente. Puedes interactuar con CrafyCAPTCHA a través de la API REST directamente, o usando uno de nuestros SDKs oficiales para simplificar la integración (validación local criptográfica).
SDKs Disponibles del lado del servidor
Métodos principales de los SDKs
Todos los SDKs del lado del servidor implementan tres métodos fundamentales:
getPublicToken()
Obtiene dinámicamente el Public Token actualizado de tu credencial. Este valor cambia
cuando tu plan se actualiza (upgrade o downgrade), por lo que no debe hardcodearse.
El Public Token obtenido debe pasarse al SDK Client-Side junto con las demás credenciales al
inicializar el widget.
createFlow(options)
Genera el encryptedIframeOptions: un string cifrado y firmado criptográficamente que
contiene el Nonce del flujo y la configuración del desafío. Este string es el que debes pasar al SDK Client-Side para que inyecte el iframe con el reto correspondiente.
Cada llamada a createFlow crea un flujo único y de un solo uso.
verifyFlow(captchaToken)
Valida el captchaToken: el string en formato base64 que el SDK Client-Side genera
automáticamente cuando el usuario completa el reto del iframe. Este token viaja al servidor como parte
del envío del formulario (por defecto en el campo CrafyCAPTCHA_token). El método
verifyFlow verifica la firma HMAC, comprueba la expiración y consume el Nonce de forma
atómica (Anti-Replay). Retorna un valor booleano: true si el token es
válido, false en caso contrario.
Los SDKs de PHP y Node.js utilizan camelCase (createFlow,
verifyFlow). El SDK de Python sigue la convención del lenguaje y utiliza
snake_case (create_flow, verify_flow). La funcionalidad
es idéntica en todos los casos.
Opciones de `createFlow`
El método createFlow acepta un objeto/array de opciones para personalizar el comportamiento
del widget de validación. Las principales propiedades son:
| Opción | Tipo | Por Defecto | Descripción |
|---|---|---|---|
mode |
String | 'auto' |
Define el comportamiento de visualización del puzzle:
|
puzzles |
Array | [] |
Establece el orden de los puzzles visuales. Si está vacío, equivale a usar:
['checkbox', 'slider', 'connect']. Mientras más alto sea el Risk Score
detectado, el usuario pasará al siguiente puzzle de la lista. También sirve para forzar un
único tipo de puzzle visual si se establece un solo nombre de puzzle en el array (ej.
['slider']).
|
1. PHP SDK
El SDK PHP facilita la creación del flow y la validación atómica. Recomendable para evitar ataques de repetición (Replay Attacks).
GitHub: crafy-captcha-php
A. Instalación
composer require crafycaptcha/crafy-captcha
B. Inicialización y Crear Flow
require __DIR__ . '/vendor/autoload.php';
use Crafy\Captcha\CrafyCAPTCHA;
// Inicializar el SDK
$crafy = new CrafyCAPTCHA(
'TU_PUBLIC_KEY',
'TU_SECRET_KEY',
"https://captcha.crafy.net/api" // Opcional, URL de la API
);
// Generar las opciones seguras para el iframe
$encryptedIframeOptions = $crafy->createFlow([
'mode' => 'puzzle', // auto | hidden | puzzle
'puzzles' => [], // [] | ['checkbox', 'slider', 'connect'] (orden personalizado de menor a mayor riesgo)
]);
// Obtener el Public Token dinámico para pasarlo al SDK Client-Side
$publicToken = $crafy->getPublicToken();
// $encryptedIframeOptions y $publicToken se pasan al inicializador de JS en el frontend
C. Validar el captchaToken (Al procesar el formulario)
$captchaToken = $_POST['CrafyCAPTCHA_token']; // El captchaToken enviado por el frontend
try {
// verifyFlow valida la firma HMAC, expiración y consume el Nonce (Anti-Replay)
$isValid = $crafy->verifyFlow($captchaToken);
if($isValid) {
// CAPTCHA Válido. Procesar login o registro...
echo "Usuario seguro.";
} else {
die("Error de validación del captcha.");
}
} catch (Exception $e) {
die("Token inválido o expirado: " . $e->getMessage());
}
D. Almacenamiento Personalizado (Caché & Nonces)
Por defecto, el SDK utiliza almacenamiento atómico en archivos temporales del sistema operativo,
garantizando 100% de compatibilidad hacia atrás. Sin embargo, para entornos descentralizados (ej.
múltiples servidores en clúster) donde la carpeta /tmp no es compartida, puedes inyectar un
motor de almacenamiento diferente.
Opción 1: Base de Datos (PDO)
Ideal si ya tienes una conexión a base de datos y no quieres guardar archivos. Primero, debes ejecutar un script de configuración por única vez para crear la tabla necesaria:
El método installSchema() se debe usar una única vez para configurar la
tabla inicial. No debes llamarlo cada vez que instancies CrafyCAPTCHA para crear o verificar un
flow.
<?php
// Archivo: setup_database.php
require __DIR__ . '/vendor/autoload.php';
use Crafy\Captcha\PDOStorage;
// Configuración de tu conexión PDO (ejemplo con MySQL/MariaDB)
$dsn = 'mysql:host=127.0.0.1;dbname=mi_app_db;charset=utf8mb4';
$user = 'root';
$password = 'tu_contraseña';
try {
$pdo = new PDO($dsn, $user, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
// Instanciamos el almacenamiento
$storage = new PDOStorage($pdo);
// Ejecutamos la creación del esquema por única vez
$storage->installSchema();
echo "✅ Tabla 'crafy_storage' y sus índices creados correctamente.\n";
} catch (PDOException $e) {
echo "❌ Error de base de datos: " . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "❌ Error general: " . $e->getMessage() . "\n";
}
Una vez creada la tabla, puedes inyectar el motor PDO en tu código de forma regular:
use Crafy\Captcha\PDOStorage;
$pdo = new PDO('mysql:host=localhost;dbname=mi_app', 'root', 'password');
// Inyectamos el motor PDO
$crafy->setStorage(new PDOStorage($pdo));
// El uso sigue siendo exactamente el mismo
$encryptedIframeOptions = $crafy->createFlow();
Opción 2: Sistema Propio (Ej. Redis / Memcached)
Puedes crear tu propia clase implementando la interfaz StorageInterface:
use Crafy\Captcha\StorageInterface;
class MiRedisStorage implements StorageInterface {
private $redis;
public function __construct($redisInstance) {
$this->redis = $redisInstance;
}
public function getCache(string $key): ?string {
return $this->redis->get($key) ?: null;
}
public function consumeNonce(string $nonce): bool {
// En Redis, un "DEL" devuelve 1 si existía, lo cual es atómico y perfecto
return $this->redis->del('nonce_' . $nonce) > 0;
}
// ... Implementar los demás métodos requeridos de StorageInterface ...
}
$crafy->setStorage(new MiRedisStorage($miConexionRedis));
2. Node.js SDK
El SDK oficial para Node.js permite integrar CrafyCAPTCHA fácilmente en aplicaciones Express, NestJS, etc.
NPM: crafy-captcha
GitHub: crafy-captcha-node
A. Instalación
npm install crafy-captcha
B. Inicialización y Crear Flow
const {CrafyCAPTCHA} = require('crafy-captcha');
async function test() {
// Inicializar el SDK
const captcha = new CrafyCAPTCHA('pk_your_public', 'sk_your_secret');
// Crear Flow y generar opciones cifradas
const encryptedIframeOptions = await captcha.createFlow({
mode: 'puzzle', // auto | hidden | puzzle
puzzles: [] // [] | ['checkbox', 'slider', 'connect'] (orden personalizado)
});
// Obtener el Public Token dinámico para pasarlo al SDK Client-Side
const publicToken = await captcha.getPublicToken();
console.log("Opciones para el iframe:", encryptedIframeOptions);
console.log("Public Token:", publicToken);
}
test();
C. Validar el captchaToken
const {CrafyCAPTCHA} = require('crafy-captcha');
const captcha = new CrafyCAPTCHA('pk_your_public', 'sk_your_secret');
// Ejemplo en una ruta Express
app.post('/login', async (req, res) => {
const captchaToken = req.body.CrafyCAPTCHA_token;
try {
const isValid = await captcha.verifyFlow(captchaToken);
if(isValid) {
res.send("Usuario seguro.");
} else {
res.status(403).send("Error de validación del captcha.");
}
} catch (error) {
res.status(500).send("Token inválido o expirado.");
}
});
D. Almacenamiento Personalizado con Adaptadores
Dado que JavaScript es asíncrono por naturaleza, el patrón es muy limpio. Para entornos distribuidos,
puedes guardar la caché y los nonces en Redis inyectando un adaptador que herede de
StorageAdapter.
const { CrafyCAPTCHA, StorageAdapter } = require('crafy-captcha');
const { createClient } = require('redis');
// 1. Creas tu propio adaptador
class RedisStorage extends StorageAdapter {
constructor(redisClient) {
super();
this.redis = redisClient;
}
async getCache(key) {
return await this.redis.get(key);
}
async setCache(key, data, expiresAt) {
// Calculamos los segundos restantes para que Redis lo auto-borre
const ttlSeconds = Math.max(0, expiresAt - Math.floor(Date.now() / 1000));
await this.redis.set(key, data, { EX: ttlSeconds });
}
async deleteCache(key) {
await this.redis.del(key);
}
async storeNonce(nonce, expiresAt) {
// Guardamos con TTL para Garbage Collection automático
const ttlSeconds = Math.max(0, Math.floor((expiresAt - Date.now()) / 1000));
await this.redis.set(`nonce_${nonce}`, '1', { EX: ttlSeconds });
}
async consumeNonce(nonce) {
// En Redis, DEL devuelve 1 si borró algo y 0 si no existía. Es atómico (Anti-Replay Attack)
const deletedCount = await this.redis.del(`nonce_${nonce}`);
return deletedCount > 0;
}
async clearAllNonces() {
const keys = await this.redis.keys('nonce_*');
if (keys.length > 0) await this.redis.del(keys);
return keys.length;
}
async gcNonces() {
// No necesitamos Garbage Collection en Redis porque le pusimos { EX: ttl }
return;
}
}
// 2. Uso en la aplicación
async function iniciarApp() {
const redisClient = createClient({ url: 'redis://localhost:6379' });
await redisClient.connect();
const captcha = new CrafyCAPTCHA('pk_...', 'sk_...');
// 3. Inyectamos tu nuevo motor de almacenamiento
captcha.setStorage(new RedisStorage(redisClient));
// 4. Todo funciona mágicamente sobre Redis
const flowOptions = await captcha.createFlow({ mode: 'invisible' });
}
iniciarApp();
3. Python SDK
El SDK oficial para Python permite integrar CrafyCAPTCHA en aplicaciones Django, Flask, FastAPI y cualquier otro framework de Python.
El SDK de Python utiliza snake_case siguiendo las convenciones del lenguaje:
create_flow() en lugar de createFlow() y verify_flow() en
lugar de verifyFlow().
PyPI: crafy-captcha
GitHub: crafy-captcha-python
A. Instalación
pip install crafy-captcha
B. Inicialización y Crear Flow
from crafy_captcha import CrafyCAPTCHA
# Inicializar el SDK
captcha = CrafyCAPTCHA('TU_PUBLIC_KEY', 'TU_SECRET_KEY')
# Generar las opciones seguras para el iframe
encrypted_iframe_options = captcha.create_flow({
'mode': 'puzzle', # auto | hidden | puzzle
'puzzles': [] # [] | ['checkbox', 'slider', 'connect']
})
# Obtener el Public Token dinámico para pasarlo al SDK Client-Side
public_token = captcha.get_public_token()
# encrypted_iframe_options y public_token se pasan al frontend
print(encrypted_iframe_options)
print(public_token)
C. Validar el captchaToken
from crafy_captcha import CrafyCAPTCHA
captcha = CrafyCAPTCHA('TU_PUBLIC_KEY', 'TU_SECRET_KEY')
# El captchaToken enviado por el frontend (ej. desde un formulario Flask/Django)
captcha_token = request.form.get('CrafyCAPTCHA_token')
try:
is_valid = captcha.verify_flow(captcha_token)
if is_valid:
print("Usuario seguro.")
else:
# Obtener el motivo exacto del fallo
motivo = captcha.get_last_flow_verify_error()
print(f"Error de validación: {motivo}")
except Exception as e:
print(f"Token inválido o expirado: {e}")
D. Almacenamiento Personalizado con Adaptadores
Si tu aplicación se ejecuta en múltiples contenedores (ej. Kubernetes o Docker), puedes usar Redis para
almacenar los datos temporalmente heredando de StorageAdapter.
import redis
import time
from crafy_captcha import CrafyCAPTCHA, StorageAdapter
# 1. Creas tu propio adaptador
class RedisStorage(StorageAdapter):
def __init__(self, redis_client):
self.redis = redis_client
def get_cache(self, key: str) -> str:
data = self.redis.get(key)
return data.decode('utf-8') if data else None
def set_cache(self, key: str, data: str, expires_at: int) -> None:
ttl = max(0, expires_at - int(time.time()))
self.redis.setex(key, ttl, data)
def delete_cache(self, key: str) -> None:
self.redis.delete(key)
def store_nonce(self, nonce: str, expires_at: int) -> None:
ttl = max(0, expires_at - int(time.time()))
self.redis.setex(f"nonce_{nonce}", ttl, "1")
def consume_nonce(self, nonce: str) -> bool:
# Atómico: delete devuelve > 0 si existía y 0 si ya fue consumido
deleted_count = self.redis.delete(f"nonce_{nonce}")
return deleted_count > 0
def clear_all_nonces(self) -> int:
keys = self.redis.keys("nonce_*")
if keys:
self.redis.delete(*keys)
return len(keys)
def gc_nonces(self) -> None:
pass # En Redis no necesitamos Garbage Collection manual (se borran solos)
# === USO EN LA APLICACIÓN ===
# 1. Conectarse a Redis
r = redis.Redis(host='localhost', port=6379, db=0)
# 2. Inicializar el SDK
captcha = CrafyCAPTCHA('pk_...', 'sk_...')
# 3. Inyectar el motor de almacenamiento
captcha.set_storage(RedisStorage(r))
# 4. El SDK ahora funcionará 100% sobre Redis de manera transparente
public_token = captcha.get_public_token()
Plugin para WordPress
Si utilizas WordPress, disponemos de un plugin oficial que facilita enormemente la integración de CrafyCAPTCHA en los formularios nativos sin necesidad de tocar código.
Instalación paso a paso
- Ir a Plugins: En el panel de administración de tu WordPress, dirígete a Plugins > Añadir nuevo plugin.
- Buscar el Plugin: En la barra de búsqueda superior, escribe CrafyCAPTCHA (puedes ver la página oficial del plugin aquí).
- Instalar y Activar: Haz clic en el botón Instalar ahora y, al finalizar la instalación, pulsa en Activar.
- Configurar: En el menú lateral izquierdo de tu WordPress, dirígete a Ajustes > CrafyCAPTCHA. Allí podrás ingresar tus credenciales (Public Key, Secret Key y Signing Public Key) que puedes obtener desde tu Panel de Control.
Una vez activado y configurado, el plugin inyectará y validará automáticamente el CAPTCHA en los
formularios estándar de WordPress, como la página de inicio de sesión (wp-login.php),
el formulario de registro y el de comentarios.
Ejemplos de Integración
Para facilitar la implementación de CrafyCAPTCHA, ofrecemos ejemplos listos para usar que puedes tomar como referencia o base para tus proyectos.
Ejemplo Completo: Frontend (JS) + Backend (PHP)
Este ejemplo demuestra paso a paso cómo integrar el widget en un formulario HTML utilizando nuestro SDK de JavaScript, y cómo recibir y validar atómicamente el token resultante en tu servidor usando nuestro SDK de PHP.
- Código Fuente (Público): Repositorio en GitHub
- Demo en Vivo: Ver Demo Funcionando
Tipos de Puzzles
Dependiendo del Risk Score detectado, el sistema puede presentar diferentes niveles de
fricción. Puedes probar cada uno de los puzzles forzando el tipo de reto en nuestras demos:
- Invisible: No hay interacción del usuario. Se resuelven pruebas criptográficas de trabajo (PoW) en segundo plano de manera totalmente invisible.
- Checkbox: Un simple clic. Evalúa la biometría del movimiento del ratón y el tiempo de reacción. Ver Demo Checkbox.
- Slider: El usuario debe deslizar una pieza para coincidir con la imagen. Ver Demo Slider.
- Connect: El usuario debe conectar números con letras en una imagen. Ver Demo Connect.
Integración con Cloudflare Turnstile
De forma predeterminada, CrafyCAPTCHA cuenta con su propio motor de seguridad y algoritmos criptográficos (Proof-of-Work). Sin embargo, puedes potenciar esta seguridad integrando Cloudflare Turnstile de forma nativa. Esta capa adicional es opcional y se configura directamente en tu Panel de Control.
Al integrar Turnstile, sumas la telemetría global y el análisis de amenazas de Cloudflare al motor de riesgo de CrafyCAPTCHA. El sistema creará y gestionará desafíos Turnstile de forma automática, brindando una experiencia invisible y respetuosa de la privacidad para los usuarios genuinos, al tiempo que detiene tráfico automatizado sofisticado.
Pasos de Sincronización
No necesitas modificar tu código; todo se gestiona desde el panel:
- Crea una cuenta en Cloudflare o inicia sesión.
- Accede a tu panel de Tokens de API.
- Haz clic en el botón Crear token y selecciona Comenzar en "Crear token personalizado".
- Asígnale un nombre, por ejemplo: CrafyCAPTCHA Token.
- En Permisos, añade estos dos permisos exactos:
- Cuenta ➔ Turnstile ➔ Editar
- Cuenta ➔ Configuración de la cuenta ➔ Leer
- Haz clic en Ir al resumen y luego en Crear token.
- Copia el Token de API generado.
- En tu Panel de Control de CrafyCAPTCHA, ve al menú de usuario, selecciona Sincronizar Cloudflare, y pega tu token.
Una vez sincronizado, CrafyCAPTCHA creará automáticamente los widgets de Turnstile necesarios en tu cuenta de Cloudflare para cada uno de tus dominios, orquestando las validaciones transparentes sin que tengas que intervenir.
Autenticación (API REST)
Si deseas consumir el API de administración o endpoints protegidos, primero debes obtener un JWT (JSON Web Token) autenticándote con tus credenciales.
Obtener Token
Endpoint: POST /api/?action=authenticate
fetch('/api/?action=authenticate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
public_key: 'TU_PUBLIC_KEY',
secret_key: 'TU_SECRET_KEY'
})
})
.then(res => res.json())
.then(data => {
if(data.status === 'success') {
console.log("Bearer Token:", data.data.token);
console.log("Public Token actualizado:", data.data.public_token);
console.log("Expira en (segundos):", data.data.expires_in);
}
});
La respuesta exitosa incluye:
| Campo | Tipo | Descripción |
|---|---|---|
token |
String | El JWT Bearer Token para autenticar llamadas posteriores a la API. |
public_token |
String | El Public Token actualizado de tu credencial. Este es el mismo valor que retorna
getPublicToken() en los SDKs.
|
expires_in |
Integer | Tiempo de vida del Bearer Token en segundos. |
Uso del Token
Para todos los demás endpoints, debes pasar el token en el Header Authorization:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsIn...
Endpoints del API
La lógica criptográfica para generar el encryptedIframeOptions (el método
createFlow) reside de forma nativa dentro de los SDKs Backend y no está
disponible como endpoint en la API REST por motivos de arquitectura descentralizada
(Zero-RTT) y seguridad. Por lo tanto, se recomienda encarecidamente utilizar uno de los SDKs oficiales, o en su defecto, hacer un port de la lógica de encriptación
a tu lenguaje de programación.
Check Flow (Validación Alternativa)
Si no puedes usar un SDK, puedes validar el token haciendo una llamada HTTP a este endpoint atómico.
Endpoint: POST /api/?action=check_flow
Autenticación: Requiere JWT Bearer
// Payload a enviar
{
"flow_token": "uuid-del-token-generado-por-el-frontend"
}
// Respuesta Exitosa
{
"status": "success",
"data": {
"valid": true,
"risk_score": 0.1,
"message": "Flow consumido atómicamente"
}
}
Nota: Este endpoint consume el token asegurando que el estado cambie a consumed.
Intentar validarlo dos veces arrojará error (previene Replay Attacks).
Rate Limits y Facturación
Para proteger nuestra infraestructura y asegurar disponibilidad para todos, CrafyCAPTCHA utiliza un modelo estricto de control de tráfico en frontera.
1. Rate Limit en Autenticación
El endpoint de Login (/api/?action=authenticate) está protegido para evitar fuerza bruta en
tus credenciales. Límite:
10 intentos por minuto por
IP, y 2 por
segundo. Exceder esto devolverá HTTP
429.
2. Límites por Usuario y Plan
El uso general del API está limitado según la cuota de tu plan de facturación activo. El plan viene incrustado en el Bearer Token (JWT) una vez que te autenticas.
| Plan | Peticiones por Minuto | Peticiones por Segundo (Ráfaga) |
|---|---|---|
| Starter | 8 req / min | 2 req / sec |
| Growth | 150 req / min | 20 req / sec |
| Scale | 500 req / min | 50 req / sec |
Al cambiar de plan (upgrade o downgrade), ten en cuenta lo siguiente:
- Public Key, Secret Key y Signing Public Key nunca cambian.
- El Public Token se regenera automáticamente. Los SDKs Backend lo gestionan
de forma dinámica a través del método
getPublicToken(). Consulta la sección Credenciales para más detalles. - Para que los nuevos Rate Limits surtan efecto en la API REST, deberás volver a autenticarte
(
/api/?action=authenticate) para obtener un nuevo Bearer Token con tus límites actualizados.
Si superas los límites de tu plan, recibirás una respuesta 429 Too Many Requests. Te
sugerimos implementar retrasos exponenciales (Exponential Backoff) en tus scripts ante estos eventos.