Modelo de segurança
O que o serviço garante, o que ele não garante, e as decisões por trás de cada escolha.
A premissa
O plugin roda na máquina do cliente. Ele não pode guardar segredo nenhum: um .jar é um zip, e quem controla a máquina lê tudo que há dentro.
Todo o desenho parte disso. Nada no sistema depende de o plugin esconder alguma coisa.
Por que assimétrico
HMAC exigiria que o verificador tivesse o mesmo segredo do emissor — ou seja, um segredo dentro de cada .jar distribuído. Com assinatura assimétrica, o plugin carrega apenas a chave pública.
Descompilar o plugin, extrair a chave e estudá-la não produz nada: forjar uma autorização exigiria a chave privada, que nunca sai da plataforma.
| Componente | Onde vive | Para que serve |
|---|---|---|
| Chave privada Ed25519 | Banco da plataforma, cifrada com AES-256-GCM | Assinar autorizações. Nunca sai. |
| Chave pública | Publicada em /.well-known/keys | Verificar. Qualquer um pode baixar. |
| Chave de licença | config.yml do cliente | Autenticar o cliente. ~98 bits de entropia. |
| Product ID | Dentro do .jar, à vista | Identificar o produto. Público e inofensivo. |
| Chave de API | Servidor do vendedor, em variável de ambiente | Gerenciar plugins e licenças. NUNCA em um .jar. |
IP e porta: pista, não prova
O endereço chega declarado pelo cliente
Então para que serve o vínculo por IP?
- Contra compartilhamento casual. Quem passa a chave para um amigo não costuma editar o plugin para mentir o IP; ele só instala e falha.
- Como trilha. Cada recusa fica registrada com o que foi declarado e com o hash do IP observado pela borda. Divergência entre os dois é exatamente o sinal que você procura no painel.
- Como limite operacional. Combinado com o teto de instalações e com a chave, torna a redistribuição cara e visível.
Onde o vínculo por IP quebra sozinho
| Cenário | O que acontece |
|---|---|
| IP dinâmico | O provedor troca o IP e a licença para de valer no meio da noite. Sem aviso, para um cliente que não fez nada errado. |
| NAT / CGNAT | Vários clientes diferentes aparecem com o mesmo IP. O vínculo deixa de distinguir qualquer coisa. |
| Hospedagem compartilhada | O painel move o servidor de máquina entre reinícios; o IP muda sozinho. |
| Proxy / Velocity | O IP que o plugin enxerga é o do proxy, não o do backend — e vários backends compartilham o mesmo. |
Por isso o padrão é vínculo por instalação
IP ou IP_PORT vê esse aviso na própria tela de configuração da política.A recusa não ensina
Chave inexistente, chave de outro produto e segredo errado devolvem a mesma resposta: license_not_valid, com o mesmo status e o mesmo corpo.
Distingui-los transformaria a rota em um oráculo: bastaria testar chaves até alguma responder “existe, mas é de outro produto”.
Os códigos de diagnóstico — license_expired, installation_limit_reached, binding_mismatch — só aparecem depois que a chave foi provada. Nesse ponto não há o que proteger: a pessoa tem a credencial, e esconder o motivo só faria o administrador legítimo abrir um chamado.
Contra reapresentação
Três mecanismos, todos obrigatórios:
requestIdúnico por resposta. O SDK confere que é o mesmo da solicitação que ele acabou de enviar.nonceecoado. Valor aleatório gerado pelo cliente a cada chamada, devolvido dentro do documento assinado. Amarra a resposta à solicitação.- Prazo dentro da assinatura.
expiresAtegraceUntilestão no payload assinado — não há como esticá-los sem invalidar a assinatura.
Rotação de chave de assinatura
Existe, a qualquer momento, no máximo uma chave ACTIVE. Rotacionar gera um par novo e move o anterior para RETIRED.
Uma chave aposentada não assina mais, mas continua publicada. É isso que permite rotacionar sem derrubar servidor de cliente: as autorizações já entregues seguem verificáveis até expirarem sozinhas.
REVOKED é o caso de comprometimento: sai da publicação na hora, e tudo que ela assinou deixa de ser verificável. É uma decisão operacional cara — por isso é uma ação separada de “aposentar”.
Revogação de licença
Revogar derruba as sessões ativas junto. Sem isso, a revogação só valeria depois que o documento assinado expirasse sozinho — até uma hora de acesso indevido.
O SDK também apaga a autorização guardada ao receber uma recusa definitiva, para que o modo offline não mantenha uma licença revogada viva até o fim do grace period.
SSRF em webhooks
Um webhook é, por definição, “a aplicação faz uma requisição para uma URL que um usuário escolheu”. Sem controle, isso é SSRF pronto.
- Apenas HTTPS, apenas porta 443.
- Resolução de DNS antes de conectar, com recusa de endereço privado, loopback, link-local, multicast e dos ranges de metadados de nuvem — incluindo
169.254.169.254e os equivalentes IPv6. - A validação roda no cadastro e de novo a cada entrega: um domínio público pode passar a resolver para
127.0.0.1depois de aprovado. - Sem seguir redirecionamento. Um 302 para endereço interno anularia tudo acima.
- Timeout curto e corpo de resposta truncado.
Como a chave de licença é guardada
Quatro derivações, com propósitos separados:
| Coluna | O que é | Para quê |
|---|---|---|
| keyHash | HMAC-SHA256 | Autenticar, em tempo constante. |
| keyLookup | HMAC com outra chave derivada | Achar a linha em O(1). Achar NÃO é autenticar: as duas derivações usam info diferente no HKDF. |
| keyEncrypted | AES-256-GCM, reversível | Permitir que o TITULAR veja a própria chave no painel. |
| keyHint | Últimos 4 caracteres | Exibir em listagem. |
Por que a chave é recuperável — e o que isso custa
O custo, dito com todas as letras: um dump do banco deixa de ser inofensivo. O que ele não custa: a cifra usa chave derivada do segredo da aplicação, que não está no banco — é preciso ter os dois. A decifra acontece em um único lugar, sob guarda de propriedade, com limite de taxa e registro de auditoria.
Limites honestos
O que nenhum sistema de licença resolve
- Quem controla o servidor pode remover a chamada de verificação do bytecode. O SDK não tenta se esconder disso, e ofuscação não mudaria o resultado — só tornaria o código impossível de auditar para quem é honesto.
- Nada impede alguém de comprar, baixar o
.jare republicá-lo em um fórum. - Um cliente determinado a não pagar vai encontrar uma forma. O objetivo é que isso custe mais trabalho do que pagar.
O que o Lodestone Licensing entrega de verdade: compartilhamento casual deixa de funcionar, há teto de instalações simultâneas, toda tentativa fica registrada com o motivo, e dá para revogar na hora — valendo na próxima verificação, não no próximo reinício.
Prometer mais que isso seria vender DRM, que não existe. O Playground inclui um cenário de resposta adulterada, que demonstra exatamente onde a proteção começa e onde ela termina.