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:
- O usuário inicia o processo de verificação.
- O widget faz uma solicitação para
challengeurl
para buscar os dados do desafio. - O widget processa e resolve o desafio com base nos dados recuperados.
- O usuário envia o formulário contendo o desafio resolvido.
- 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:
-
Usando
challengeurl
: Se configurado, o widget busca o desafio na URL especificada. O servidor deve retornar um novo desafio conforme descrito abaixo. -
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 clientesecret_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 JSONresponse = { 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 algoritmoalg_ok = equals(data.algorithm, 'SHA-256');
// Validar desafiochallenge_ok = equals(data.challenge, sha2_hex(concat(data.salt, data.number)))
// Validar assinaturasignature_ok = equals(data.signature, hmac_sha2_hex(data.challenge, hmac_key))
// Considerar a solicitação verificada se todas as verificações forem verdadeirasverified = alg_ok and challenge_ok and signature_ok
Exemplo
A biblioteca JS oficial funciona com Node.js, Bun e Deno.
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âmetroexpires
. Seu servidor deve então verificar a expiração durante o processo de verificação.