Validação DNSSEC em Go: Escrevi meu próprio validador e não enlouqueci completamente

Validação DNSSEC em Go: Escrevi meu próprio validador e não enlouqueci completamente

Um desenvolvedor compartilha sua experiência na criação de um validador DNSSEC em Go para o VantageDNS, abordando desafios como a cadeia de confiança, algoritmos, problemas de tempo e a importância da validação para a segurança DNS. O artigo detalha o processo de validação, as armadilhas encontradas e as lições aprendidas.

MundiX News·31 de maio de 2026·10 min de leitura·👁 17 views

Olá novamente, Habr!

Estou desenvolvendo o VantageDNS, um resolvedor DNS recursivo com foco em privacidade e filtragem. A parte da borda (edge) é feita em Go, com 10 nós ao redor do mundo, usando miekg/dns por baixo dos panos. Em algum momento, minhas desculpas acabaram e tive que escrever um validador DNSSEC. Com minhas próprias mãos. À noite. Depois da oitava xícara de café.

A seguir, vou explicar como funciona a cadeia de confiança, o que existe na biblioteca padrão, quais armadilhas estão no caminho e por que ainda evito o algoritmo 14 como um gato no quintal. No final, há links para a implementação de código aberto, você pode dar uma olhada.

Por que DNSSEC?

Simplificando, o DNSSEC resolve duas tarefas. A primeira é a proteção contra envenenamento de cache. Ataque Kaminsky, lembra? Ele completou 17 anos em 2026, e ainda há muitas surpresas: toda vez que há uma notícia sobre um "incidente BGP redirecionando o tráfego", há um resolvedor sem validação por perto. A segunda tarefa é verificar a origem: você garante que a resposta foi realmente assinada pelo proprietário da zona, e não por um sujeito em um café com openwrt e más intenções.

Na prática, de acordo com minhas medições nos nós de borda, cerca de 95% dos domínios em TLDs .com/.net ainda não têm RRSIG. Principalmente grandes serviços, bancos, serviços públicos e paranóicos estão assinados. Mas para cenários críticos, o DNSSEC é realmente necessário. E se você está construindo um resolvedor a sério, você precisa validar.

Primeiro, tentei ignorá-lo. A realidade, como de costume, me bateu na cabeça com um porrete.

Cadeia de Confiança em uma Página

Explicarei brevemente para que o código seja mais claro.

O DNSSEC é construído como uma hierarquia de assinaturas do registro solicitado para a zona raiz. Em cada nível, existem três entidades-chave.

  • DNSKEY - chaves públicas da zona. Existem dois tipos: KSK (Key Signing Key, flag 257) assina apenas o conjunto de registros DNSKEY. ZSK (Zone Signing Key, flag 256) assina os outros RRSet.
  • RRSIG - na verdade, a assinatura. Cada RRSet em uma zona assinada tem seu próprio RRSIG.
  • DS (Delegation Signer) está no pai. Este é o hash do KSK do filho. O pai diz: "esta chave do meu filho é real".

Validação = ir de baixo para cima:

Recebemos

A example.com e RRSIG A example.com

Solicitamos DNSKEY example.com , encontramos o ZSK com o KeyTag necessário, verificamos a assinatura.

Solicitamos DNSKEY example.com como um rrset inteiro, verificamos sua assinatura KSK.

Solicitamos DS example.com em .com , verificamos se o hash KSK corresponde.

Em seguida, validamos o RRSIG no DS em .com através do DNSKEY em .com e assim por diante até a raiz.

A DNSKEY raiz é verificada em relação à âncora de confiança, a chave pública da IANA, que você embutiu no código ou puxou de um arquivo.

A âncora de confiança é o único ponto onde a confiança vem "de fora". Todo o resto é criptografia.

O que existe em miekg/dns

A biblioteca github.com/miekg/dns é a base. Ele analisa o formato wire, calcula o KeyTag, implementa RRSIG.Verify() para todos os algoritmos ativos. Eu a adoro, como adoro um gato velho: pelo fato de existir e não exigir explicações.

Mas o próprio algoritmo de validação não está lá. E isso é bom. A lógica crítica de segurança deve estar em seu código para que você entenda onde está errando, e não ore para uma caixa preta. miekg/dns dá primitivos, você precisa montar a cadeia sozinho.

Algoritmo passo a passo

Primeiro, a validação de um RRSet. Esta é a base sobre a qual tudo o mais é construído.

go
func (r *Resolver) ValidateRRSet(rrset []dns.RR, rrsigs []*dns.RRSIG, keyset []*dns.DNSKEY) error {
    if len(rrsigs) == 0 {
        return ErrNoSignature
    }
    now := time.Now().Unix()

    for _, rrsig := range rrsigs {
        if int64(rrsig.Inception) > now || int64(rrsig.Expiration) < now {
            continue
        }

        for _, k := range keyset {
            if k.KeyTag() != rrsig.KeyTag {
                continue
            }
            if k.Algorithm != rrsig.Algorithm {
                continue
            }
            if err := rrsig.Verify(k, rrset); err == nil {
                return nil
            }
        }
    }
    return ErrNoValidSig
}

Em seguida, uma ascensão recursiva na cadeia. A ideia é simples: para a zona Z , verificamos o conjunto de registros DNSKEY (KSK assina DNSKEY, ZSK assina todo o resto), então solicitamos o DS do pai e garantimos que o KSK do filho corresponda ao DS.

Função completa ValidateChain (versão simplificada)

go
func (r *Resolver) ValidateChain(ctx context.Context, name string, qtype uint16) error {
    rrset, rrsigs, err := r.fetchSigned(ctx, name, qtype)
    if err != nil {
        return err
    }

    zone := signerName(rrsigs)
    if zone == "" {
        return ErrNoSigner
    }

    keyset, keysigs, err := r.fetchSigned(ctx, zone, dns.TypeDNSKEY)
    if err != nil {
        return err
    }
    keys := toDNSKEY(keyset)

    if err := r.ValidateRRSet(rrset, rrsigs, keys); err != nil {
        return fmt.Errorf("rrset %s: %w", name, err)
    }
    if err := r.ValidateRRSet(keyset, keysigs, keys); err != nil {
        return fmt.Errorf("dnskey %s: %w", zone, err)
    }

    if zone == "." {
        return r.matchTrustAnchor(keys)
    }

    parent := dns.Fqdn(strings.SplitN(zone, ".", 2)[1])
    dsset, dssigs, err := r.fetchSigned(ctx, zone, dns.TypeDS)
    if err != nil {
        return err
    }

    if err := r.matchDS(keys, dsset); err != nil {
        return err
    }

    parentKeys, _, err := r.cachedKeys(ctx, parent)
    if err != nil {
        return err
    }
    if err := r.ValidateRRSet(dsset, dssigs, parentKeys); err != nil {
        return fmt.Errorf("ds %s: %w", zone, err)
    }

    return r.ValidateChain(ctx, parent, dns.TypeDNSKEY)
}

A versão real no repositório é mais longa: há cache de DNSKEY/DS intermediários, processamento de CNAME (a zona mudou, a cadeia vai em um novo ramo), backoff em erros de rede. Mas o núcleo é assim.

Eu mantenho o DS raiz como uma estrutura hardcoded com a capacidade de substituir por um arquivo. RFC 5011, por enquanto, é simplificado, falaremos sobre isso mais tarde.

Armadilhas que encontrei

Algoritmos

Em 2026, os algoritmos ativos para DNSSEC são os seguintes.

  • Algoritmo 8, RSASHA256. O antigo workhorse, ainda a maioria das zonas assinadas.
  • Algoritmo 13, ECDSA P-256 com SHA-256. O padrão moderno, a raiz já o usa.
  • Algoritmo 15, Ed25519. Está crescendo, mas lentamente.
  • O algoritmo 14 (ECDSA P-384) na natureza é encontrado com menos frequência do que um meteoro caindo. Eu o suporto formalmente, mas se você não tiver tempo, comece sem ele. RSAMD5 e RSASHA1 devem ser rejeitados como inseguros.

Desvio de tempo

O RRSIG tem os campos Inception e Expiration em segundos Unix. Se o NTP do nó de borda estiver desatualizado em mais de alguns minutos, a validação falha, e silenciosamente: você vê ErrNoValidSig e pensa que a zona está quebrada.

História real: um dos meus nós com broken systemd-timesyncd validou o DNSSEC para alguns usuários por uma semana. Percebi quando recebi um ticket "meu banco não abre, o do vizinho abre". Fui olhar, timedatectl mostra "System clock synchronized: no", e o relógio está 12 minutos adiantado.

Desde então, tenho uma verificação de desvio no healthcheck contra duas fontes NTP independentes e a métrica dnssec_validation_clock_skew_seconds em Prometheus. Sem flags, eu mesmo testo, o que é bastante humilhante, mas funciona.

NSEC e NSEC3

Uma história separada, respostas negativas. "O domínio não existe" também precisa ser provado criptograficamente, caso contrário, o invasor simplesmente dirá "não há tal registro" e contornará a validação.

NSEC - uma lista vinculada de nomes na zona, classificados canonicamente. A zona assina "entre aaa.example.com e ccc.example.com não há nada", e você garante que bbb.example.com realmente se enquadra nesse intervalo.

NSEC3 é organizado da mesma forma, mas com nomes hash para que você não possa usar a caminhada da zona para listar todos os domínios na zona. Implementar saltos no espaço hash é uma tarefa separada para algumas noites. Eu honestamente adiei a validação completa do NSEC3 para v2, na primeira versão eu valido o NSEC e para o fallback do NSEC3 em inseguro.

Validadores soltos e cadeias quebradas

Na realidade, muitas zonas são assinadas incorretamente. O caso mais comum: a zona principal é assinada, ela tem um CNAME para um domínio de outra pessoa, e não há assinaturas lá. Formalmente, de acordo com o RFC, você deve retornar SERVFAIL.

Na prática, se você é um resolvedor para usuários finais, SERVFAIL significa "minha internet não está funcionando", e o usuário vai para 8.8.8.8. Portanto, eu fiz o modo permissive : com uma cadeia quebrada, eu registro e retorno uma resposta insegura com uma nota no log de consulta. Os paranóicos têm o modo estrito no perfil.

Rotação da âncora de confiança

A ICANN rotacionou a raiz KSK em 2018, e muitos resolvedores caíram porque seus operadores esqueceram de atualizar a âncora de confiança. Desde então, existe o RFC 5011, automated rollover: o resolvedor pega a nova KSK sozinho, se ela for assinada pela antiga, e após um período de espera confia na nova.

Na primeira versão, eu não fiz o RFC 5011 completamente, eu simplesmente tenho um arquivo trust-anchors.xml (o mesmo que a IANA publica), que é puxado quando o binário de borda é atualizado. Isso funciona enquanto você lança versões com mais frequência do que a ICANN gira as chaves. Ou seja, sempre. Mas para uma implantação séria, o 5011 precisa ser finalizado, e eu o finalizarei assim que o sprint atual for concluído.

O que eu não fiz na primeira versão e me arrependi

Cache de validações negativas. Cada NXDOMAIN foi validado novamente, todos os NSEC/NSEC3 foram analisados novamente, todas as assinaturas foram verificadas novamente. No fluxo de tráfego de um nó de borda típico, isso consumiu muito CPU e adicionou latência aos domínios "inexistentes", que, como você sabe, são muitos na natureza (todo malware-DGA, erros de digitação, telemetria de serviços falhos).

É tratado por um cache negativo separado, onde a chave é (qname, qtype, NSEC-proof-hash) . Fiz na segunda iteração, a latência no NXDOMAIN caiu drasticamente.

O segundo erro, não otimizado criptografia. Um RRSet pode ter 5-10 RRSIG (a zona usa várias chaves ou altera o algoritmo). Eu os percorri em sequência, verificando todos. Em um grande conjunto de registros DNSKEY com RSA, isso é perceptível. Solução: primeiro filtre por KeyTag/Algorithm, depois por tempo, e só então a cara verificação.

Habilitar por padrão

Eu tenho a validação DNSSEC opt-in: perfil dnssec_validation: true . No plano gratuito, está desativado por padrão por dois motivos.

Primeiro, a maioria dos usuários no plano gratuito simplesmente não se importa. Eles precisam de "publicidade não exibida, e graças a Deus". Se algo começar a SERVFAIL, haverá reclamações no suporte, eles não têm tempo nem desejo de lidar com uma cadeia quebrada.

Segundo, eu mesmo ainda tenho casos de borda que aparecem uma vez por semana. Alguém reclama que algum site regional não está resolvendo. Você vai olhar, eles têm um KSK com o algoritmo 14 e a expiração foi ontem. O operador da zona está dormindo. Você não pode fazer nada.

No plano pago, planejo torná-lo default-on em 6 meses, quando todos esses casos forem catalogados e eu aprender a contorná-los ou explicar de forma argumentativa "seu DNSSEC está quebrado, vá para o administrador".

Como testar

Existem algumas zonas de teste públicas: dnssec-failed.org , especialmente com uma assinatura quebrada, deve SERVFAIL. dnssec-tools.org , devidamente assinado, deve resolver. internetsociety.org , um grande site real com DNSSEC, bom para um teste rápido. dig +dnssec @dns.vantagedns.com/ dnssec-failed.org dig +dnssec @dns.vantagedns.com/ dnssec-tools.org A

No primeiro caso, esperamos status: SERVFAIL . No segundo, status: NOERROR e a flag ad (Authenticated Data) na resposta. Se não houver a flag ad , a validação não funcionou, vá olhar os logs.

Nos testes unitários, uso respostas wire-format hardcoded de testdata/ , gerei-as via dig +dnssec +noall +answer e salvei. Assim, os testes não dependem da rede externa e não falham quando as assinaturas da zona de teste expiram.

Onde está o código

Estou preparando meu próprio módulo DNSSEC para abrir, mas o repositório ainda é privado - há um mecanismo de lista de bloqueio e um remetente por perto, que ainda não foram separados por limites limpos, eles têm as especificidades operacionais de especificamente meus 10 nós. Se você quiser dar uma olhada em um fragmento específico (validador, carregador de âncora de confiança, NSEC3 walk) para seu sistema - escreva para ms@vantagedns.com , enviarei um tarball com testes. Serei especialmente grato por comentários sobre NSEC3 e o algoritmo 14, confessei honestamente que essas são as fraquezas.

O que eu entendi

DNSSEC é mais complicado do que parece na Wikipédia e mais fácil do que assustam nas conferências. Se você está construindo seu próprio resolvedor, não ignore, mas não escreva um validador no primeiro mês. Primeiro, um cache normal, respostas negativas, ECS, filtragem. Quando a parte básica funciona e não falha sob carga, então DNSSEC. Nesse ponto, você já terá a infraestrutura para testes, métricas e alertas, sem os quais o validador se transformará em uma caixa preta com um humor.

E, por favor, coloque um NTP normal em todos os nós. Eu falo sério.

Links

VantageDNS: vantagedns.com

Tente: vantagedns.com/try

Kit de imprensa e diagrama de arquitetura: vantagedns.com/press-kit

🛡️⚡

Pare de pesquisar. Comece a hackear.

O MundiX é seu copiloto de pentest com IA: comandos exatos, análise de outputs e próximo passo na kill chain — em segundos.

Testar grátis por 7 dias →

Sem cartão para começar · Planos a partir de R$49/mês

📤 Compartilhar & Baixar

🧰 Ferramentas recomendadas

Divulgação: alguns links são patrocinados. Podemos receber comissão se você comprar — sem custo extra para você. Só indicamos o que faz sentido para a comunidade.

Aprendendo Kali Linux: Teste de segurança, pentest e hacking ético

Aprendendo Kali Linux: Teste de segurança, pentest e hacking ético

Com centenas de ferramentas pré-instaladas, a distribuição Kali Linux facilita o trabalho de os profissionais de segurança começarem a fazer testes de segurança rapidamente. No entanto, com mais de 600 ferramentas em seu arsenal, o Kali Linux também pode ser desafiador. A nova edição deste prático livro abrange as atualizações nas ferramentas e inclui uma melhor abordagem da análise forense e da engenharia reversa. Ric Messier, autor, não fica apenas no teste de segurança, mas também faz uma abordagem sobre a execução de análise forense, incluindo a análise em disco e na memória, assim como alguma análise básica de malware. • Explore as diversas ferramentas disponíveis no Kali Linux • Entenda o valor do teste de segurança e examine os tipos de teste disponíveis • Aprenda os aspectos básicos do pentest em todo o ciclo de vida do ataque • Instale o Kali Linux em vários sistemas, tanto físicos quanto virtuais • Descubra como usar diferentes ferramentas destinadas à segurança • Estruture um teste de segurança baseado nas ferramentas do Kali Linux • Estenda as ferramentas do Kali para criar técnicas de ataque avançadas • Use o Kali Linux para ajudar a criar relatórios quando o teste terminar “A abordagem concisa, clara e baseada na experiência adotada por Ric Messier para a introdução do Kali Linux e dos testes de cibersegurança é incomparável. Este livro é uma leitura excelente e acessível para iniciantes e um recurso valioso para qualquer pessoa.” —Alexander Arlt, Consultor sênior de segurança, Google

Ver na Amazon
Gshield 2 em 1 Hub Extensor Conector USB-C + USB-A e Adaptador de Rede Ethernet LAN RJ45 com 3 Entradas USB 3.0 até 5 Gbps em Liga de Alumínio para Computador e Notebook, Cinza

Gshield 2 em 1 Hub Extensor Conector USB-C + USB-A e Adaptador de Rede Ethernet LAN RJ45 com 3 Entradas USB 3.0 até 5 Gbps em Liga de Alumínio para Computador e Notebook, Cinza

Compatível com portas USB-C e USB-A, ideal para ampliar a conectividade de dispositivos como MacBook Pro e outros com portas USB-C. Inclui um adaptador USB-A extra, proporcionando uma conexão Ethernet estável e veloz de até 1 Gbps, perfeita para filmes, jogos online e videoconferências. Oferece três portas USB 3.0 com velocidades de transferência de até 5 Gbps, permitindo conectar mouse, teclado, discos rígidos e outros periféricos. Fabricado em alumínio durável, garantindo longa vida útil e resistência ao uso diário. Design compacto e leve, ideal para viagens de negócios e uso diário, facilitando o transporte e armazenamento. Funciona com Windows 10/8.1/8, Mac OS e Chrome OS, oferecendo versatilidade incomparável para diversas necessidades de conectividade. Assegura uma conectividade estável e rápida, perfeita para tarefas exigentes como transferência de dados, streaming e mais.

Ver na Amazon
Hacking APIs: Breaking Web Application Programming Interfaces

Hacking APIs: Breaking Web Application Programming Interfaces

Hacking APIs is a crash course on web API security testing that will prepare you to penetration-test APIs, reap high rewards on bug bounty programs, and make your own APIs more secure. You'll learn how REST and GraphQL APIs work in the wild and set up a streamlined API testing lab with Burp Suite and Postman. Then you'll master tools useful for reconnaissance, endpoint analysis, and fuzzing, such as Kiterunner and OWASP Amass. Next, you'll learn to perform common attacks, like those targeting an API's authentication mechanisms and the injection vulnerabilities commonly found in web applications. You'll also learn techniques for bypassing protections against these attacks. In the book's nine guided labs, which target intentionally vulnerable APIs, you'll practice: Enumerating APIs users and endpoints using fuzzing techniques Using Postman to discover an excessive data exposure vulnerability Performing a JSON Web Token attack against an API authentication process Combining multiple API attack techniques to perform a NoSQL injection Attacking a GraphQL API to uncover a broken object level authorization vulnerability

Ver oferta
Gray Hat Hacking: The Ethical Hacker's Handbook, Sixth Edition

Gray Hat Hacking: The Ethical Hacker's Handbook, Sixth Edition

Up-to-date strategies for thwarting the latest, most insidious network attacks This fully updated, industry-standard security resource shows, step by step, how to fortify computer networks by learning and applying effective ethical hacking techniques. Based on curricula developed by the authors at major security conferences and colleges, the book features actionable planning and analysis methods as well as practical steps for identifying and combating both targeted and opportunistic attacks. Gray Hat Hacking: The Ethical Hacker's Handbook, Sixth Edition clearly explains the enemy's devious weapons, skills, and tactics and offers field-tested remedies, case studies, and testing labs. You will get complete coverage of Internet of Things, mobile, and Cloud security along with penetration testing, malware analysis, and reverse engineering techniques. State-of-the-art malware, ransomware, and system exploits are thoroughly explained. Fully revised content includes 7 new chapters covering the latest threats Includes proof-of-concept code stored on the GitHub repository Authors train attendees at major security conferences, including RSA, Black Hat, Defcon, and B-Sides

Ver na Amazon
Bloqueador USB de privacidade de porta USB para PC, notebook, bloco de laptop,

Bloqueador USB de privacidade de porta USB para PC, notebook, bloco de laptop,

Proteção de privacidade aprimorada: protege o link de transmissão de dados para evitar roubo de informações, fornecendo proteção de segurança robusta que protege a privacidade do usuário durante transferências de arquivos e garante uma conexão segura para interações de dispositivos sem preocupações em vários ambientes Uso a longo prazo: a camada protetora resistente ao desgaste, combinada com um corpo de metal resistente, oferece gerenciamento de calor confiável e qualidade duradoura durante o uso diário Entrega eficiente de energia: a tecnologia de chip inteligente garante a identificação automática dos requisitos de energia, fornecendo carregamento eficiente alinhando-se com vários protocolos de carregamento rápido para maior conveniência Proteção contra sobrecarga: evitando riscos de sobrecarga, este bloqueador de dados USB protege a vida útil da bateria e garante um desempenho estável, mantendo um fluxo estável de energia para melhorar a longevidade do dispositivo de forma eficaz Prático de transportar: com atenção à portabilidade, este bloqueador de dados USB oferece um design compacto que é leve e fácil de transportar, melhorando a conveniência do usuário e operação eficiente

Ver na Amazon

📩 Newsletter MundiX

Receba novidades de cibersegurança + um checklist de pentest grátis. Sem spam.

Ao assinar você concorda em receber e-mails. Cancele quando quiser.