Saltearse al contenido

Integración del Servidor

Proceso de verificación PoW

El proceso de verificación se basa en el trabajo computacional (SHA-hashing) e implica los siguientes pasos:

  1. El usuario inicia el proceso de verificación.
  2. El widget realiza una solicitud a challengeurl para obtener los datos del desafío.
  3. El widget procesa y resuelve el desafío basado en los datos recuperados.
  4. El usuario envía el formulario que contiene el desafío resuelto.
  5. El servidor verifica la carga útil en el envío del formulario para asegurar que coincida con la solución esperada del desafío.

El aspecto del lado del cliente es gestionado por el widget ALTCHA, mientras que se requiere la implementación de la verificación en el lado del servidor. El manejador de envíos del servidor (por ejemplo, POST /form_submit) necesita autenticar el payload de ALTCHA al enviar el formulario.

Consulte la documentación de prueba de trabajo para obtener más información sobre el mecanismo detrás de ALTCHA.

Complejidad

Generar un nuevo desafío implica tres pasadas de cálculo de SHA (una vez para el desafío y dos veces para la firma HMAC). De manera similar, verificar una solución también requiere tres pasadas.

El rango del número aleatorio ajusta la dificultad de la tarea computacional requerida por el cliente.

Para más detalles, consulte ajustando la complejidad.

Servidor

El servidor debe generar un nuevo desafío para cada verificación de ALTCHA. Hay dos métodos para proporcionar el desafío al widget:

  1. Utilizando challengeurl: Si está configurado, el widget obtiene el desafío desde la URL especificada. El servidor debe devolver un nuevo desafío como se describe a continuación.

  2. Utilizando challengejson: Proporcione directamente el desafío como una cadena codificada en JSON. Este método es útil para páginas renderizadas en el servidor.

Lea más sobre el mecanismo de prueba de trabajo.

Creando un desafío

Aquí hay un ejemplo de pseudo-código para crear un desafío en el servidor:

// Elija la complejidad: el número aleatorio máximo (consulte la documentación de complejidad)
maxnumber = 100_000;
// Genere una sal aleatoria (longitud recomendada: al menos 10 caracteres)
salt = random_string();
// Genere un número secreto aleatorio (entero positivo)
// Rango entre 0...maxnumber
// Asegúrese de NO exponer este número al cliente
secret_number = random_int(maxnumber);
// Calcule el hash SHA-256 de la concatenación de salt + secret_number (resultado codificado como cadena HEX)
challenge = sha2_hex(concat(salt, secret_number));
// Cree una firma de servidor usando una clave HMAC (resultado codificado como cadena HEX)
signature = hmac_sha2_hex(challenge, hmac_key);
// Devuelva los datos codificados en JSON
response = {
algorithm: 'SHA-256',
challenge,
maxnumber,
salt,
signature,
};

Cuando el parámetro maxnumber se devuelve al cliente, el widget ALTCHA puede optimizar mejor la distribución de la carga de trabajo entre varios trabajadores, haciéndolo funcionar más rápido. Las bibliotecas oficiales devuelven el valor maxnumber por defecto, ya que se recomienda generalmente. Dependiendo de su caso de uso, el parámetro maxnumber podría considerarse un parámetro secreto que no debe exponerse al cliente, ya que revela el rango del número. En casos específicos donde intencionalmente desea una alta dificultad, como M2M o integraciones personalizadas, no devuelva maxnumber.

Parámetros de Sal

A partir de la versión del Widget v0.4 (Mayo de 2024), se recomienda incluir el indicador expires y parámetros adicionales en el salt como cadenas de consulta codificadas en URL. Esto le permite pasar datos personalizados, que formarán parte de la firma y serán verificables en el servidor.

El widget detecta automáticamente el parámetro expires cuando se proporciona en el salt como una marca de tiempo Unix en segundos:

salt = '<random_salt>?expires=1715526540'

Para garantizar la compatibilidad con futuras versiones del widget, se recomienda agregar un guion bajo _ como prefijo a cualquier parámetro personalizado.

La biblioteca altcha-lib ya admite parámetros de sal y verifica automáticamente la expiración del desafío desde la versión v0.3.

Envío del formulario

Al enviar dentro de un <form>, el servidor recibirá datos codificados como application/x-www-form-urlencoded o multipart/form-data, dependiendo de la estructura del formulario. El payload de ALTCHA estará incrustado como el campo altcha (personalizable a través del atributo name en el widget).

Use el valor del campo altcha como el payload en los ejemplos a continuación. El payload es una cadena codificada Base64-JSON.

Validación de la solución

Aquí hay un ejemplo de pseudo-código para validar una solución en el servidor:

// El payload es una cadena codificada en BASE64-JSON
// Los datos decodificados son un objeto que contiene { algoritmo, desafío, número, salt, firma }
data = json_decode(base64_decode(payload));
// Validar algoritmo
alg_ok = equals(data.algorithm, 'SHA-256');
// Validar desafío
challenge_ok = equals(data.challenge, sha2_hex(concat(data.salt, data.number)))
// Validar firma
signature_ok = equals(data.signature, hmac_sha2_hex(data.challenge, hmac_key))
// Considerar la solicitud verificada si todas las comprobaciones son verdaderas
verified = alg_ok and challenge_ok and signature_ok

Ejemplo

La librería JS oficial funciona con Node.js, Bun y Deno.

server.js
import { createChallenge, verifySolution } from 'altcha-lib';
const hmacKey = '$ecret.key'; // Cambie la clave secreta HMAC
// Crear un nuevo desafío y enviarlo al cliente:
const challenge = await createChallenge({
hmacKey,
});
// Al enviar, verificar el payload:
const ok = await verifySolution(payload, hmacKey);

Para más ejemplos e integraciones, consulte la página de Integraciones de la Comunidad.

Recomendaciones de seguridad

  • Ataques de repetición

    Para prevenir la vulnerabilidad de “ataques de repetición”, donde un cliente reenvía la misma solución varias veces, el servidor debe implementar medidas que invaliden desafíos previamente resueltos.

    El servidor debe mantener un registro de desafíos resueltos y rechazar cualquier envío que intente reutilizar un desafío que ya ha sido resuelto con éxito.

  • Caducidad del desafío

    Limitar el período de validez de los desafíos puede fortalecer las medidas de seguridad, asegurando que los desafíos no puedan ser explotados indefinidamente. Implementar la caducidad del desafío implica establecer un límite de tiempo dentro del cual un desafío debe ser resuelto y enviado.

    Un método efectivo para lograr la caducidad del desafío implica incorporar un sello de tiempo del servidor en la salt del desafío durante su generación.

    A partir de la versión 0.4 del widget, puede utilizar parámetros de sal e incluir el parámetro ?expires=<unix_ts> en el salt. El widget detectará automáticamente el parámetro expires. Su servidor deberá entonces verificar la expiración durante el proceso de verificación.