Dominando Segurança em DNS: DNSSEC, DNS over HTTPS e Proteção contra Poisoning em Projetos Reais Já leu

O Problema Fundamental do DNS O Domain Name System (DNS) é um dos protocolos mais críticos da internet. Ele traduz nomes de domínio legíveis (como ) em endereços IP (como ) que os computadores entendem. No entanto, desde sua criação em 1987, o DNS foi projetado com uma premissa questionável: confiança implícita. Não há mecanismo nativo que garanta a autenticidade das respostas recebidas. Isso cria uma janela de oportunidade para atacantes. Se alguém conseguir interceptar uma consulta DNS e enviar uma resposta falsa antes da resposta legítima chegar, pode redirecionar o usuário para um servidor malicioso. Este é o famoso DNS Poisoning (envenenamento de DNS). Existem várias técnicas para explorar essa vulnerabilidade, desde ataques man-in-the-middle até exploração de falhas em servidores DNS recursivos mal configurados. Para entender segurança em DNS, você precisa dominar três camadas de proteção que trabalham juntas: validação criptográfica (DNSSEC), criptografia em trânsito (DNS over HTTPS) e práticas defensivas. Vamos explorar cada uma detalhadamente. DNSSEC: Autenticação Criptográfica

O Problema Fundamental do DNS

O Domain Name System (DNS) é um dos protocolos mais críticos da internet. Ele traduz nomes de domínio legíveis (como google.com) em endereços IP (como 142.250.185.46) que os computadores entendem. No entanto, desde sua criação em 1987, o DNS foi projetado com uma premissa questionável: confiança implícita. Não há mecanismo nativo que garanta a autenticidade das respostas recebidas.

Isso cria uma janela de oportunidade para atacantes. Se alguém conseguir interceptar uma consulta DNS e enviar uma resposta falsa antes da resposta legítima chegar, pode redirecionar o usuário para um servidor malicioso. Este é o famoso DNS Poisoning (envenenamento de DNS). Existem várias técnicas para explorar essa vulnerabilidade, desde ataques man-in-the-middle até exploração de falhas em servidores DNS recursivos mal configurados.

Para entender segurança em DNS, você precisa dominar três camadas de proteção que trabalham juntas: validação criptográfica (DNSSEC), criptografia em trânsito (DNS over HTTPS) e práticas defensivas. Vamos explorar cada uma detalhadamente.

DNSSEC: Autenticação Criptográfica de Respostas DNS

O que é DNSSEC?

DNSSEC (DNS Security Extensions) é um conjunto de extensões do DNS que adiciona autenticação criptográfica às respostas. A ideia central é simples: o operador de cada zona DNS assina digitalmente seus registros usando criptografia de chave pública. Quando um resolver recebe uma resposta, pode verificar a assinatura e confirmar que os dados não foram alterados.

O fluxo funciona em camadas. A raiz (root zone) é assinada com uma chave privada. Essa chave privada existe apenas em um local altamente seguro. A zona .com é assinada pela raiz usando o mecanismo de delegação segura. Cada domínio individual (como example.com) é assinado pelos nameservers autorizados. Quando você consulta um registro, o resolver segue essa cadeia de confiança da raiz até o domínio específico.

Componentes Técnicos do DNSSEC

DNSSEC utiliza dois tipos principais de chaves: KSK (Key Signing Key) e ZSK (Zone Signing Key). A KSK é a chave master que assina outras chaves. A ZSK é usada para assinar os registros reais da zona. Essa separação permite rotação de chaves sem quebrar a cadeia de confiança.

Existem dois novos tipos de registro DNS no DNSSEC:

  • RRSIG: Contém a assinatura criptográfica de um conjunto de registros
  • DNSKEY: Publica a chave pública usada para verificar as assinaturas
  • DS (Delegation Signer): Permite que a zona pai (como .com) confie na zona filha (como example.com) através de um hash da chave
  • NSEC/NSEC3: Fornece prova criptográfica de não-existência de registros

Quando um resolver validador recebe uma resposta DNS, ele segue este processo:

  1. Verifica se o registro vem com uma assinatura RRSIG
  2. Extrai a chave pública (DNSKEY) da mesma zona
  3. Valida a assinatura usando a chave pública
  4. Verifica se a chave pública em si é confiável comparando com o registro DS da zona pai
  5. Repete este processo até chegar à raiz (root zone), cuja confiança é pré-configurada no resolver

Implementação Prática: Verificando DNSSEC com dig

Você pode testar DNSSEC usando a ferramenta dig. Instale-a (geralmente disponível em bind-utils ou dnsutils):

# Verificar se um domínio tem DNSSEC ativado
dig example.com +dnssec +short

# Resultado típico: você verá flags "ad" (authenticated data)
# e registros RRSIG na resposta

# Validar especificamente a cadeia DNSSEC
dig example.com +dnssec @8.8.8.8

# Ver os registros DNSKEY da zona
dig example.com DNSKEY +short

# Ver o registro DS (delegação segura)
dig example.com DS +short

Implementação em Python: Validador DNSSEC

Aqui está um exemplo funcional de validação DNSSEC usando a biblioteca dnspython:

import dns.resolver
import dns.rdatatype
import dns.dnssec
import dns.rrset

def validate_dnssec(domain_name):
    """
    Valida a cadeia DNSSEC de um domínio.
    Retorna True se válido, False caso contrário.
    """

    resolver = dns.resolver.Resolver()

    try:
        # Obtém a resposta com DNSSEC validado
        answers = resolver.resolve(
            domain_name, 
            dns.rdatatype.A,
            raise_on_no_answer=False
        )

        # Verifica se a resposta tem autenticação
        if answers.response.flags & dns.flags.AD:
            print(f"✓ {domain_name}: DNSSEC validado (AD flag presente)")
            return True
        else:
            print(f"✗ {domain_name}: DNSSEC não validado ou não configurado")
            return False

    except Exception as e:
        print(f"Erro ao validar {domain_name}: {e}")
        return False

def check_dnssec_chain(domain_name):
    """
    Examina a cadeia completa de DNSSEC.
    """
    resolver = dns.resolver.Resolver()

    try:
        # Busca DNSKEY records (chaves públicas)
        dnskeys = resolver.resolve(domain_name, 'DNSKEY')
        print(f"\nDNSKEY records para {domain_name}:")
        for rrset in dnskeys.rrset:
            print(f"  Flag: {rrset.flags}, Algorithm: {rrset.algorithm}")

        # Busca RRSIG records (assinaturas)
        answers = resolver.resolve(domain_name, 'A')
        for rrset in answers.rrset:
            print(f"Registros A encontrados: {rrset}")

        # Busca DS record (delegação segura da zona pai)
        try:
            ds_records = resolver.resolve(domain_name, 'DS')
            print(f"\nDS records encontrados: {len(ds_records)}")
            for record in ds_records:
                print(f"  Key Tag: {record.key_tag}")
        except dns.resolver.NXDOMAIN:
            print(f"\nNenhum DS record encontrado (zona raiz ou não delegada)")

    except Exception as e:
        print(f"Erro ao verificar cadeia: {e}")

# Uso
if __name__ == "__main__":
    validate_dnssec("google.com")
    validate_dnssec("example.com")
    check_dnssec_chain("github.com")

Para instalar a dependência:

pip install dnspython

Quando você rodar este código, verá quais domínios têm DNSSEC validado. O Google e o GitHub são bons exemplos de domínios que implementam DNSSEC completamente.

Limitações do DNSSEC

DNSSEC não é uma solução perfeita. Ele garante autenticidade e integridade dos dados, mas não oferece confidencialidade: qualquer pessoa na rede pode ver que domínios você está consultando. Além disso, DNSSEC adiciona complexidade operacional significativa: necessita rotação regular de chaves, tem overhead computacional na validação, e bugs em implementações são comuns. Um erro na configuração do DNSSEC pode deixar toda uma zona inacessível (problema real enfrentado por muitos operadores).

DNS over HTTPS (DoH): Criptografia em Trânsito

Por Que Precisamos de DoH?

Embora DNSSEC valide a integridade das respostas DNS, ele não fornece confidencialidade. Em uma conexão DNS tradicional (porta 53, UDP ou TCP), as consultas trafegam em texto plano. Qualquer pessoa na rede (seu ISP, operador de WiFi, governo em uma rede corporativa) pode ver exatamente quais sites você acessa.

DNS over HTTPS (DoH) resolve este problema ao encapsular as consultas DNS dentro de requisições HTTPS. Como HTTPS é criptografado end-to-end, o servidor DoH não consegue ver qual domínio você está consultando apenas observando a conexão (embora ainda veja que você está se conectando a um servidor DoH).

Diferença Entre DoH e DoT

Existe outra alternativa menos conhecida: DNS over TLS (DoT), que usa TLS puro (porta 853) em vez de HTTPS. DoH é mais prático porque passa por proxies HTTP/HTTPS convencionais e não requer porta especial, mas DoT é tecnicamente mais eficiente. Para este artigo, focaremos em DoH por ser mais amplamente adotado.

Implementação em Python: Cliente DoH

Aqui está como fazer consultas DNS over HTTPS em Python:

import requests
import json

class DoHClient:
    """
    Cliente para consultas DNS over HTTPS.
    """

    def __init__(self, doh_server="https://dns.google/dns-query"):
        """
        Inicializa o cliente DoH.

        Servidores DoH populares:
        - Google: https://dns.google/dns-query
        - Cloudflare: https://1.1.1.1/dns-query
        - Quad9: https://9.9.9.9/dns-query
        """
        self.doh_server = doh_server
        self.session = requests.Session()

    def query(self, domain, query_type="A"):
        """
        Realiza uma consulta DNS via HTTPS.

        Args:
            domain: Nome de domínio (ex: 'google.com')
            query_type: Tipo de registro DNS (A, AAAA, MX, TXT, etc)

        Returns:
            Lista de respostas ou None em caso de erro
        """

        # Converte tipo de registro para número
        type_map = {
            "A": 1, "AAAA": 28, "MX": 15, "TXT": 16, "NS": 2,
            "CNAME": 5, "SOA": 6, "SRV": 33
        }

        if query_type not in type_map:
            print(f"Tipo de registro desconhecido: {query_type}")
            return None

        # Prepara os parâmetros
        params = {
            "name": domain,
            "type": type_map[query_type],
            "do": True,  # Ativa validação DNSSEC (se disponível)
        }

        # Headers padrão
        headers = {
            "Accept": "application/dns-json",
            "User-Agent": "DoH-Client/1.0"
        }

        try:
            # Faz a requisição GET (também pode usar POST)
            response = self.session.get(
                self.doh_server,
                params=params,
                headers=headers,
                timeout=5
            )

            if response.status_code != 200:
                print(f"Erro: Status {response.status_code}")
                return None

            # Parse da resposta JSON
            data = response.json()

            # Extrai os resultados
            answers = []
            if "Answer" in data:
                for answer in data["Answer"]:
                    answers.append({
                        "name": answer.get("name"),
                        "type": answer.get("type"),
                        "ttl": answer.get("TTL"),
                        "data": answer.get("data")
                    })

            # Verifica se DNSSEC foi validado
            if data.get("AD"):  # AD = Authenticated Data flag
                print(f"✓ Resposta DNSSEC validada")

            return answers

        except requests.exceptions.RequestException as e:
            print(f"Erro na requisição: {e}")
            return None

    def bulk_query(self, domains, query_type="A"):
        """
        Realiza múltiplas consultas de forma eficiente.
        """
        results = {}
        for domain in domains:
            print(f"\nConsultando {domain}...")
            results[domain] = self.query(domain, query_type)
        return results


# Uso prático
if __name__ == "__main__":
    # Cria cliente DoH
    client = DoHClient(doh_server="https://dns.google/dns-query")

    # Consulta simples
    print("=== Consulta A record ===")
    results = client.query("example.com", "A")
    if results:
        for result in results:
            print(f"Domain: {result['name']} -> {result['data']}")

    # Consulta MX
    print("\n=== Consulta MX records ===")
    mx_results = client.query("google.com", "MX")
    if mx_results:
        for result in mx_results:
            print(f"MX: {result['data']} (TTL: {result['ttl']})")

    # Múltiplas consultas
    print("\n=== Consultas em lote ===")
    domains = ["github.com", "stackoverflow.com", "wikipedia.org"]
    bulk = client.bulk_query(domains, "A")

    for domain, answers in bulk.items():
        if answers:
            for answer in answers:
                print(f"{domain}: {answer['data']}")

Execute com:

pip install requests
python doh_client.py

Implementação em JavaScript/Node.js: DoH Cliente

Se você trabalha com JavaScript, pode usar a biblioteca node-fetch ou nativa fetch:

class DoHClient {
    constructor(dohServer = "https://dns.google/dns-query") {
        this.dohServer = dohServer;
    }

    /**
     * Realiza consulta DNS via HTTPS
     * @param {string} domain - Nome de domínio
     * @param {string} queryType - Tipo de registro (A, AAAA, MX, TXT, etc)
     * @returns {Promise<Array>} Lista de respostas
     */
    async query(domain, queryType = "A") {
        const typeMap = {
            "A": 1, "AAAA": 28, "MX": 15, "TXT": 16, 
            "NS": 2, "CNAME": 5, "SOA": 6, "SRV": 33
        };

        if (!typeMap[queryType]) {
            console.error(`Tipo desconhecido: ${queryType}`);
            return null;
        }

        const params = new URLSearchParams({
            name: domain,
            type: typeMap[queryType],
            do: true  // DNSSEC
        });

        try {
            const response = await fetch(
                `${this.dohServer}?${params}`,
                {
                    method: 'GET',
                    headers: {
                        'Accept': 'application/dns-json'
                    }
                }
            );

            if (!response.ok) {
                console.error(`Erro: Status ${response.status}`);
                return null;
            }

            const data = await response.json();

            // Extrai respostas
            const answers = [];
            if (data.Answer) {
                for (const answer of data.Answer) {
                    answers.push({
                        name: answer.name,
                        type: answer.type,
                        ttl: answer.TTL,
                        data: answer.data
                    });
                }
            }

            // Verifica DNSSEC
            if (data.AD) {
                console.log("✓ DNSSEC validado");
            }

            return answers;
        } catch (error) {
            console.error(`Erro: ${error.message}`);
            return null;
        }
    }

    /**
     * Consulta múltiplos domínios
     */
    async bulkQuery(domains, queryType = "A") {
        const results = {};
        for (const domain of domains) {
            console.log(`Consultando ${domain}...`);
            results[domain] = await this.query(domain, queryType);
        }
        return results;
    }
}

// Uso
(async () => {
    const client = new DoHClient("https://dns.google/dns-query");

    console.log("=== Consulta A ===");
    const results = await client.query("example.com", "A");
    if (results) {
        results.forEach(r => {
            console.log(`${r.name}: ${r.data}`);
        });
    }

    console.log("\n=== Consulta MX ===");
    const mx = await client.query("google.com", "MX");
    if (mx) {
        mx.forEach(r => console.log(`MX: ${r.data}`));
    }

    console.log("\n=== Bulk Query ===");
    const bulk = await client.bulkQuery(
        ["github.com", "stackoverflow.com", "wikipedia.org"],
        "A"
    );

    for (const [domain, answers] of Object.entries(bulk)) {
        if (answers) {
            answers.forEach(a => console.log(`${domain}: ${a.data}`));
        }
    }
})();

Desvantagens e Privacidade em DoH

Embora DoH criptografe as consultas DNS, é importante entender suas limitações. O servidor DoH ainda sabe quais domínios você consulta (ele precisa processar a requisição). Além disso, análise de tráfego sofisticada ainda pode inferir comportamento baseada em padrões temporais e tamanho de pacotes. Para máxima privacialidade, considere combinar DoH com VPN ou Tor.

Proteção Contra DNS Poisoning

Tipos de Ataques de Poisoning

DNS Poisoning (envenenamento de DNS) é qualquer ataque que resulte em uma resposta DNS falsa sendo entregue ao cliente. Os principais tipos são:

  1. Cache Poisoning: Atacante injeta registros falsos no cache de um resolver recursivo. Uma vez que estão em cache, todos os usuários que usam aquele resolver recebem respostas falsa.

  2. Man-in-the-Middle (MITM): Atacante intercept a consulta DNS e responde antes do servidor legítimo. Funciona especialmente bem em redes WiFi abertas ou onde há falta de validação.

  3. DNS Spoofing via Authoritative Server: Comprometimento de um servidor DNS autoritativo permite ao atacante responder falsamente para consultas de seu domínio.

  4. UDP Flood/DDoS: Ataque de negação de serviço que torna o servidor DNS indisponível. Embora não seja poisoning exato, degrada DNS significativamente.

Defesas Implementadas: Rate Limiting e Source Port Randomization

Os servidores DNS modernos implementam várias defesas. A randomização de porta de origem (source port randomization) aumenta o espaço de adivinhação de respostas de 2^16 para 2^32. A limitação de taxa (rate limiting) reduz o impacto de ataques brutos. Os cookies DNS adicionam verificação de autenticidade sem necessidade de DNSSEC.

Implementação em Python: Detector de Poisoning

Aqui está um sistema que ajuda a detectar possível DNS poisoning:

import dns.resolver
import dns.rdatatype
from collections import defaultdict
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

class DNSPoisoningDetector:
    """
    Detecta possível DNS poisoning comparando respostas de múltiplos resolvers.
    """

    def __init__(self, resolvers=None):
        """
        Inicializa com lista de resolvers públicos para comparação.

        Resolvers públicos confiáveis:
        - Google: 8.8.8.8, 8.8.4.4
        - Cloudflare: 1.1.1.1, 1.0.0.1
        - Quad9: 9.9.9.9, 149.112.112.112
        - OpenDNS: 208.67.222.222, 208.67.220.220
        """
        self.resolvers = resolvers or [
            "8.8.8.8",      # Google
            "1.1.1.1",      # Cloudflare
            "9.9.9.9",      # Quad9
            "208.67.222.222" # OpenDNS
        ]
        self.results = defaultdict(list)

    def query_resolver(self, resolver_ip, domain, record_type="A"):
        """
        Consulta um resolver específico.
        """
        try:
            resolver = dns.resolver.Resolver()
            resolver.nameservers = [resolver_ip]
            resolver.timeout = 5
            resolver.lifetime = 5

            answers = resolver.resolve(domain, record_type)

            # Extrai endereços IP das respostas
            ips = [str(rdata) for rdata in answers]

            return {
                "resolver": resolver_ip,
                "domain": domain,
                "ips": ips,
                "timestamp": time.time(),
                "success": True
            }

        except dns.resolver.NXDOMAIN:
            return {
                "resolver": resolver_ip,
                "domain": domain,
                "error": "NXDOMAIN",
                "success": False
            }

        except Exception as e:
            return {
                "resolver": resolver_ip,
                "domain": domain,
                "error": str(e),
                "success": False
            }

    def detect_poisoning(self, domain, record_type="A", consensus_threshold=0.7):
        """
        Detecta poisoning consultando múltiplos resolvers.

        Args:
            domain: Domínio a verificar
            record_type: Tipo de registro (A, AAAA, MX, etc)
            consensus_threshold: Percentual de resolvers que devem concordar

        Returns:
            Dicionário com análise de poisoning
        """

        print(f"\n[*] Analisando {domain} em {len(self.resolvers)} resolvers...")

        # Consulta todos os resolvers em paralelo
        results = []
        with ThreadPoolExecutor(max_workers=4) as executor:
            futures = {
                executor.submit(
                    self.query_resolver, 
                    resolver, 
                    domain, 
                    record_type
                ): resolver 
                for resolver in self.resolvers
            }

            for future in as_completed(futures):
                result = future.result()
                results.append(result)

                if result["success"]:
                    print(f"  {result['resolver']}: {result['ips']}")
                else:
                    print(f"  {result['resolver']}: Erro - {result['error']}")

        # Análise de consenso
        successful_queries = [r for r in results if r["success"]]

        if not successful_queries:
            return {
                "domain": domain,
                "poisoning_detected": False,
                "reason": "Nenhuma resposta bem-sucedida",
                "recommendation": "Verificar conectividade"
            }

        # Agrupa respostas por IP
        ip_groups = defaultdict(list)
        for result in successful_queries:
            ip_key = tuple(sorted(result["ips"]))
            ip_groups[ip_key].append(result["resolver"])

        # Encontra resposta de consenso
        consensus = max(ip_groups.items(), key=lambda x: len(x[1]))
        consensus_ips, consensus_resolvers = consensus
        consensus_rate = len(consensus_resolvers) / len(successful_queries)

        # Detecta anomalias
        anomalies = []
        if consensus_rate < consensus_threshold:
            anomalies.append({
                "type": "LOW_CONSENSUS",
                "severity": "MEDIUM",
                "description": f"Apenas {consensus_rate*100:.1f}% dos resolvers concordam"
            })

        # Verifica respostas discordantes
        if len(ip_groups) > 1:
            discordant_ips = [ips for ips in ip_groups.keys() if ips != consensus_ips]
            anomalies.append({
                "type": "DIVERGENT_RESPONSES",
                "severity": "HIGH",
                "description": f"Encontradas {len(discordant_ips)} respostas diferentes",
                "discordant_ips": [list(ips) for ips in discordant_ips]
            })

        return {
            "domain": domain,
            "poisoning_detected": len(anomalies) > 0,
            "consensus_ips": list(consensus_ips),
            "consensus_rate": f"{consensus_rate*100:.1f}%",
            "consensus_resolvers": consensus_resolvers,
            "total_resolvers_queried": len(self.resolvers),
            "successful_queries": len(successful_queries),
            "anomalies": anomalies,
            "raw_results": results
        }

    def batch_analysis(self, domains, record_type="A"):
        """
        Analisa múltiplos domínios simultaneamente.
        """
        print(f"\n[*] Iniciando análise em lote de {len(domains)} domínios")
        analysis_results = {}

        for domain in domains:
            analysis_results[domain] = self.detect_poisoning(domain, record_type)

        return analysis_results


# Uso prático
if __name__ == "__main__":
    detector = DNSPoisoningDetector()

    # Análise de domínio único
    print("=" * 60)
    print("ANÁLISE DE POISONING - DOMÍNIO ÚNICO")
    print("=" * 60)

    result = detector.detect_poisoning("google.com")

    print(f"\n[RESULTADO] {result['domain']}")
    print(f"Poisoning Detectado: {result['poisoning_detected']}")
    print(f"Taxa de Consenso: {result['consensus_rate']}")
    print(f"IPs de Consenso: {result['consensus_ips']}")

    if result['anomalies']:
        print(f"\n[ANOMALIAS ENCONTRADAS]")
        for anomaly in result['anomalies']:
            print(f"  - {anomaly['type']} (Severidade: {anomaly['severity']})")
            print(f"    {anomaly['description']}")

    # Análise em lote
    print("\n" + "=" * 60)
    print("ANÁLISE EM LOTE")
    print("=" * 60)

    domains_to_check = [
        "github.com",
        "stackoverflow.com",
        "example.com"
    ]

    batch_results = detector.batch_analysis(domains_to_check)

    print("\n[RESUMO]")
    for domain, analysis in batch_results.items():
        status = "⚠️ SUSPEITO" if analysis['poisoning_detected'] else "✓ SEGURO"
        print(f"{domain}: {status} ({analysis['consensus_rate']} consenso)")

Este detector realiza consultas paralelas a múltiplos resolvers públicos e compara as respostas. Se houver divergência significativa, algo pode estar errado.

Boas Práticas para Proteger-se

  1. Use DNSSEC: Habilite validação DNSSEC em seu resolver
  2. Implemente DoH/DoT: Criptografe consultas DNS
  3. Configure resolvers confiáveis: Use DNS de provedores reputados
  4. Monitore divergências: Use ferramentas como a que demonstrei para detectar anomalias
  5. Mantenha sistemas atualizados: DNS servers precisam de patches de segurança

Conclusão

Dominar segurança em DNS significa entender três camadas complementares: DNSSEC fornece autenticação criptográfica das respostas, garantindo que dados DNS não foram modificados em trânsito; DNS over HTTPS criptografa as próprias consultas, prevenindo espionagem do seu histórico de navegação; e defesas contra DNS Poisoning (através de múltiplos resolvers, rate limiting e validação) reduzem a superfície de ataque de protocolos legados.

O ponto crítico é que nenhuma solução sozinha é suficiente. Um domínio com DNSSEC mas sem DoH ainda expõe seu histórico de navegação. DNS over HTTPS sem DNSSEC pode ser vítima de cache poisoning. A segurança real vem de combinar essas três camadas. Em 2024, a implementação ideal é: (1) usar um resolver que valide DNSSEC, (2) fazer consultas DNS via DoH ou DoT, e (3) monitorar anomalias periodicamente. Para administradores, adicione rate limiting, source port randomization e considere implementar DNS cookies nos seus servidores.

A indústria ainda está em transição. Nem todos os domínios têm DNSSEC, nem todos os clientes usam DoH nativamente. Mas os conceitos que estudamos aqui são o futuro da segurança DNS e cada vez mais organizações responsáveis os implementam.

Referências


Artigos relacionados