Pular para o conteúdo

Integração de Servidor

Processo de verificação PoW

O processo de verificação baseia-se no trabalho computacional (SHA-hashing) e envolve as seguintes etapas:

  1. O usuário inicia o processo de verificação.
  2. O widget faz uma solicitação para challengeurl para buscar os dados do desafio.
  3. O widget processa e resolve o desafio com base nos dados recuperados.
  4. O usuário envia o formulário contendo o desafio resolvido.
  5. O servidor verifica o payload na submissão do formulário para garantir que corresponda à solução esperada do desafio.

O aspecto do lado do cliente é gerenciado pelo widget ALTCHA, enquanto a implementação da verificação no lado do servidor é necessária. O manipulador de envio do servidor (por exemplo, POST /form_submit) precisa autenticar o payload do ALTCHA ao enviar o formulário.

Consulte a documentação de prova de trabalho para ler mais sobre o mecanismo por trás do ALTCHA.

Complexidade

Gerar um novo desafio envolve três passagens de computação SHA (uma vez para o desafio e duas vezes para a assinatura HMAC). Da mesma forma, verificar uma solução também requer três passagens.

O intervalo do número aleatório ajusta a dificuldade da tarefa computacional requerida pelo cliente.

Para mais detalhes, consulte ajustando a complexidade.

Servidor

O servidor deve gerar um novo desafio para cada verificação de ALTCHA. Existem dois métodos para fornecer o desafio ao widget:

  1. Usando challengeurl: Se configurado, o widget busca o desafio na URL especificada. O servidor deve retornar um novo desafio conforme descrito abaixo.

  2. Usando challengejson: Forneça diretamente o desafio como uma string codificada em JSON. Este método é útil para páginas renderizadas pelo servidor.

Leia mais sobre o mecanismo de prova de trabalho.

Criando um desafio

Aqui está um exemplo de pseudo-código para criar um desafio no servidor:

// Escolha a complexidade - o número aleatório máximo (consulte a documentação de complexidade)
maxnumber = 100_000;
// Gere um sal aleatório (comprimento recomendado: pelo menos 10 caracteres)
salt = random_string();
// Gere um número secreto aleatório (inteiro positivo)
// Intervalo entre 0...maxnumber
// Certifique-se de NÃO expor este número ao cliente
secret_number = random_int(maxnumber);
// Calcule o hash SHA-256 do sal concatenado + secret_number (resultado codificado como string HEX)
challenge = sha2_hex(concat(salt, secret_number));
// Crie uma assinatura do servidor usando uma chave HMAC (resultado codificado como string HEX)
signature = hmac_sha2_hex(challenge, hmac_key);
// Retorne os dados codificados em JSON
response = {
algorithm: 'SHA-256',
challenge,
maxnumber,
salt,
signature,
};

Quando o parâmetro maxnumber é retornado ao cliente, o widget ALTCHA pode otimizar melhor a distribuição da carga de trabalho entre vários trabalhadores, tornando-o mais rápido. As bibliotecas oficiais retornam o valor maxnumber por padrão, pois é geralmente recomendado. Dependendo do seu caso de uso, o parâmetro maxnumber pode ser considerado um parâmetro secreto, que não deve ser exposto ao cliente, pois revela o intervalo do número. Em casos específicos onde você deseja intencionalmente uma alta dificuldade, como M2M ou integrações personalizadas, não retorne maxnumber.

Parâmetros de Sal

A partir da versão do Widget v0.4 (maio de 2024), é recomendável incluir a bandeira expires e parâmetros adicionais no salt como strings de consulta codificadas em URL. Isso permite passar dados personalizados, que farão parte da assinatura e poderão ser verificados no servidor.

O widget detecta automaticamente o parâmetro expires quando fornecido no sal como um timestamp Unix em segundos:

salt = '<random_salt>?expires=1715526540'

Para garantir a compatibilidade com futuras versões do widget, é recomendável prefixar quaisquer parâmetros personalizados com um sublinhado _.

A biblioteca altcha-lib já suporta parâmetros de sal e verifica automaticamente a expira ção do desafio a partir da versão v0.3.

Envio de formulário

Após submissão dentro de um <form>, o servidor receberá dados codificados como application/x-www-form-urlencoded ou multipart/form-data, dependendo da estrutura do formulário. O payload ALTCHA será incorporado como o campo altcha (customizável via o atributo name no widget).

Use o valor do campo altcha como o payload nos exemplos abaixo. O payload é uma string codificada em Base64-JSON.

Validação de solução

Aqui está um exemplo de pseudo-código para validar uma solução no servidor:

// O payload é uma string codificada BASE64-JSON
// Os dados decodificados são um objeto contendo { algoritmo, desafio, número, sal, assinatura }
data = json_decode(base64_decode(payload));
// Validar algoritmo
alg_ok = equals(data.algorithm, 'SHA-256');
// Validar desafio
challenge_ok = equals(data.challenge, sha2_hex(concat(data.salt, data.number)))
// Validar assinatura
signature_ok = equals(data.signature, hmac_sha2_hex(data.challenge, hmac_key))
// Considerar a solicitação verificada se todas as verificações forem verdadeiras
verified = alg_ok and challenge_ok and signature_ok

Exemplo

A biblioteca JS oficial funciona com Node.js, Bun e Deno.

server.js
import { createChallenge, verifySolution } from 'altcha-lib';
const hmacKey = '$ecret.key'; // Altere a chave HMAC secreta
// Criar um novo desafio e enviá-lo para o cliente:
const challenge = await createChallenge({
hmacKey,
});
// Ao ser enviado, validar o payload:
const ok = await verifySolution(payload, hmacKey);

Para mais exemplos e integrações, consulte a página de Integrações da Comunidade.

Recomendações de Segurança

  • Ataques de Repetição

    Para prevenir a vulnerabilidade de “ataques de repetição”, onde um cliente reenvia a mesma solução várias vezes, o servidor deve implementar medidas que invalidem desafios previamente resolvidos.

    O servidor deve manter um registro de desafios resolvidos e rejeitar quaisquer submissões que tentem reutilizar um desafio que já tenha sido resolvido com sucesso.

  • Expiração de Desafio

    Limitar o período de validade dos desafios pode reforçar as medidas de segurança, garantindo que os desafios não possam ser explorados indefinidamente. Implementar a expiração do desafio envolve definir um limite de tempo no qual um desafio deve ser resolvido e submetido.

    Um método eficaz para alcançar a expiração do desafio envolve incorporar um timestamp do servidor no salt do desafio durante sua geração.

    A partir da versão 0.4 do widget, você pode utilizar parâmetros de sal e incluir o parâmetro ?expires=<unix_ts> no sal. O widget detectará automaticamente o parâmetro expires. Seu servidor deve então verificar a expiração durante o processo de verificação.