Type something to search...
How to audit your DNS records for security risks?

How to audit your DNS records for security risks?

To audit your DNS records for security risks, start by exporting a complete inventory of every zone and record you own, then work through a checklist: look for dangling records that point at cloud resources or IP addresses you no longer control, remove records that leak internal hostnames or private IPs, check that SPF, DKIM and DMARC are strict enough to stop spoofing, add CAA records and DNSSEC, confirm your nameservers refuse zone transfers and recursive queries, and finally review the security of your registrar and DNS provider accounts. Repeat the audit regularly, because most DNS risk comes from records that were fine when they were created and quietly went stale.

DNS zones tend to grow for years without anyone pruning them. Every marketing campaign, trial service and decommissioned server can leave a record behind, and some of those leftovers are exactly what attackers look for. In this guide you'll get a practical, repeatable audit process, with dig commands and a Python script you can run against your own domains, and a summary checklist at the end.

Before You Start

You'll need:

  • Access to every DNS provider you use: Many organisations have zones spread across a registrar, a CDN and a cloud provider.
  • A list of every domain you own: Check your registrar accounts, including old ones, and your finance records for renewals.
  • dig: Available on macOS and Linux, and on Windows through BIND's tools or WSL.
  • Python 3 with dnspython: For the automated checks later (pip install dnspython).
  • Permission: Only audit domains you own or are authorised to test.

Step 1: Build a Complete Inventory

You can't audit records you can't see. DNS doesn't offer a way to list every name in a zone from the outside, so get the full zone from your provider.

  • Export the zone file: Most providers offer a BIND-format export in the dashboard or through their API.
  • Use the API for many zones: For example, with Cloudflare's API:
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=500" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  | jq -r '.result[] | [.name, .type, .content, .ttl] | @tsv'
  • Find forgotten zones: Search Certificate Transparency logs for hostnames on your domains that don't appear in any zone you know about. They often reveal subdomains hosted at another provider:
curl -s 'https://crt.sh/?q=%25.example.com&output=json' \
  | jq -r '.[].name_value' | tr 'A-Z' 'a-z' | sort -u

Save the inventory as a spreadsheet or CSV with columns for name, type, value, TTL, owner and purpose. The owner and purpose columns are the ones that make an audit useful, because a record nobody can explain is a record to investigate.

Step 2: Check Your Delegation and Nameservers

Confirm that the nameservers listed at the registry match the ones in your zone, and that they all serve the same data.

# What the parent (.com) says your nameservers are
dig +short NS example.com @a.gtld-servers.net

# What your own zone says
dig +short NS example.com

# Check each nameserver returns the same SOA serial
for ns in $(dig +short NS example.com); do
  echo "$ns $(dig +short SOA example.com @"$ns" | awk '{print $3}')"
done

Output should look like this, with identical serials:

ns1.dnsprovider.example. 2026102001
ns2.dnsprovider.example. 2026102001

Watch for:

  • Nameservers at a provider you no longer use: If a delegated nameserver belongs to a DNS service where you've closed your account, someone else might be able to claim your zone there.
  • Nameservers on a domain that's expired or about to: If your nameserver hostnames sit on a separate domain, that domain is now critical infrastructure.
  • Mismatched serials: A secondary that has stopped updating serves stale data.
  • Lame delegations: A listed nameserver that doesn't answer authoritatively for your zone.

Step 3: Hunt for Dangling Records

Dangling records are the most serious finding in most audits. They point at something that no longer exists or that you no longer control, which can let an attacker take over the hostname.

Dangling CNAMEs

A CNAME to a cloud service, such as a storage bucket, an app platform or a CDN, becomes dangerous when the resource it points at is deleted but the CNAME remains. If the provider lets anyone create a resource with that name, an attacker can claim it and serve content on your subdomain.

Check each CNAME's target:

dig +short CNAME shop.example.com
dig shop-example.azurewebsites.net A

A target that returns NXDOMAIN is a red flag:

;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 51203

Not every NXDOMAIN is exploitable, but every one is a record that serves no purpose and should be removed or fixed.

Stale A and AAAA Records

Cloud IP addresses are recycled. If you released an elastic or public IP but kept the A record, the address may now belong to another customer, and your hostname serves their content. Compare every A and AAAA record against the IPs currently allocated in your cloud accounts. Any record pointing at an address you can't account for should be removed.

Stale MX and NS Records

An MX record pointing at a mail service you've cancelled can let someone who signs up to that service receive your email. NS records delegating a subdomain to a provider you've left can let someone claim that subdomain's zone. Treat these like dangling CNAMEs.

Step 4: Look for Information Leaks

Public DNS is public. Anything in it is visible to anyone who asks or who finds the name.

  • Private IP addresses: Records such as vpn-internal.example.com A 10.0.4.20 reveal your internal network layout. Internal names belong in an internal zone, using split-horizon DNS if needed.
  • Revealing hostnames: Names like jenkins-old, test-db, admin-panel or staging-unprotected tell attackers exactly where to look. Make sure such services aren't publicly reachable, and rename or remove records that aren't needed.
  • Old verification TXT records: Domain verification tokens for services you no longer use reveal your past vendors and add clutter. Most can be deleted once verification is complete, but check the provider's documentation first, as some services re-check periodically.
  • HINFO and descriptive TXT records: Records describing operating systems or software versions are rarely needed today.

Step 5: Review Email Authentication Records

Weak email records make your domain easy to spoof.

dig +short TXT example.com | grep -i 'v=spf1'
dig +short TXT _dmarc.example.com
dig +short MX example.com

Check that:

  • There's exactly one SPF record: Two v=spf1 records cause SPF to fail.
  • SPF ends in -all or ~all: Never +all, which authorises the whole internet, and preferably not ?all.
  • SPF only includes services you still use: Remove include: entries for old providers. Each one is a third party that can send as you.
  • SPF stays within 10 DNS lookups: Exceeding the limit causes a permanent error.
  • DMARC exists and is enforced: p=quarantine or p=reject once you've reviewed your reports. A policy of p=none only monitors.
  • DKIM selectors are current: Remove selectors for services you no longer use, and make sure active keys are at least 2048-bit RSA where the service allows it.
  • Non-sending domains are locked down: Every domain and subdomain that doesn't send email should have v=spf1 -all, a DMARC record with p=reject, and ideally a null MX record (MX 0 .).

Step 6: Check CAA and DNSSEC

CAA records restrict which certificate authorities can issue certificates for your domain:

dig +short CAA example.com
0 issue "letsencrypt.org"
0 iodef "mailto:security@example.com"

No output means any public CA may issue for your domain. Add CAA records listing only the CAs you actually use.

DNSSEC protects your records from being forged in transit:

dig +short DS example.com
dig @1.1.1.1 example.com A +dnssec | grep flags

A DS record and the ad flag in the second command's output mean DNSSEC is working. If you've enabled DNSSEC, also check that signatures aren't close to expiring, which tools such as DNSViz show clearly.

Step 7: Test Your Nameservers' Behaviour

Zone Transfers Should Be Refused

A nameserver that allows zone transfers (AXFR) to anyone gives away your entire inventory:

for ns in $(dig +short NS example.com); do
  echo "== $ns"
  dig @"$ns" example.com AXFR +short | head -5
done

The safe result is ; Transfer failed. for every nameserver. If records come back, restrict transfers to your secondary servers' IPs or TSIG keys.

Authoritative Servers Shouldn't Recurse

If you run your own authoritative nameservers, they shouldn't resolve names for other domains. An open resolver can be abused in amplification attacks:

dig @ns1.example.com example.net A

The answer should have status REFUSED, or no answer with the warning recursion requested but not available.

Step 8: Review TTLs and Wildcards

  • Very long TTLs on critical records: A TTL of a week on your main A record means a compromise or mistake takes a week to clear from caches. A few minutes to an hour is a reasonable range for most records.
  • Wildcard records: A wildcard such as *.example.com makes every possible subdomain resolve. Combined with a misconfigured web server, it can make it easier to host phishing content on hostnames you never created. Use wildcards only where you need them.

Step 9: Audit the Accounts, Not Just the Records

The biggest DNS risk is someone logging in and changing your records. Check:

  • Two-factor authentication on every registrar and DNS provider account, preferably with security keys.
  • Who has access: Remove former staff and agencies. Use individual accounts rather than shared logins.
  • API tokens: Revoke old tokens, and scope remaining ones to the zones and permissions they need.
  • Registrar lock: Confirm clientTransferProhibited is set, and consider registry lock for critical domains.
  • Contact details: Make sure renewal and security emails go to a monitored shared address, not a former employee.
  • Expiry dates: Turn on auto-renew and check payment details.

You can check lock status and expiry through RDAP, the modern replacement for WHOIS:

curl -sL https://rdap.org/domain/example.com \
  | jq '{status: .status, events: [.events[] | {(.eventAction): .eventDate}]}'

Automating the Checks

Running the same commands by hand on every audit gets old quickly. This script takes a list of hostnames from your inventory and flags the most common problems:

#!/usr/bin/env python3
"""Flag common DNS security issues for a list of hostnames."""
import ipaddress
import sys

import dns.exception
import dns.resolver

def query(name: str, rtype: str):
    """Return a list of answers, or None if the name does not exist."""
    try:
        return [r.to_text() for r in dns.resolver.resolve(name, rtype, lifetime=5)]
    except dns.resolver.NXDOMAIN:
        return None
    except (dns.resolver.NoAnswer, dns.resolver.NoNameservers, dns.exception.Timeout):
        return []

def check_host(host: str) -> list[str]:
    issues = []
    cname = query(host, "CNAME")
    if cname:
        target = cname[0]
        if query(target, "A") is None and query(target, "AAAA") is None:
            issues.append(f"dangling CNAME -> {target} (NXDOMAIN)")
    for rtype in ("A", "AAAA"):
        for addr in query(host, rtype) or []:
            ip = ipaddress.ip_address(addr)
            if ip.is_private or ip.is_loopback or ip.is_link_local:
                issues.append(f"private address in public DNS: {addr}")
    return issues

def check_domain(domain: str) -> list[str]:
    issues = []
    txt = query(domain, "TXT") or []
    spf = [t for t in txt if t.strip('"').lower().startswith("v=spf1")]
    if len(spf) == 0:
        issues.append("no SPF record")
    elif len(spf) > 1:
        issues.append("multiple SPF records")
    elif "+all" in spf[0]:
        issues.append("SPF uses +all")
    dmarc = query(f"_dmarc.{domain}", "TXT") or []
    if not any("v=DMARC1" in d for d in dmarc):
        issues.append("no DMARC record")
    elif any("p=none" in d.replace(" ", "") for d in dmarc):
        issues.append("DMARC policy is p=none")
    if not query(domain, "CAA"):
        issues.append("no CAA record")
    if not query(domain, "DS"):
        issues.append("DNSSEC not enabled (no DS record)")
    return issues

if __name__ == "__main__":
    domain, hosts_file = sys.argv[1], sys.argv[2]
    for issue in check_domain(domain):
        print(f"{domain}: {issue}")
    with open(hosts_file) as f:
        for line in f:
            host = line.strip()
            if host:
                for issue in check_host(host):
                    print(f"{host}: {issue}")

Run it with your apex domain and a file of hostnames, one per line:

python3 dns_audit.py example.com hosts.txt
example.com: DMARC policy is p=none
example.com: no CAA record
promo.example.com: dangling CNAME -> example-promo.s3-website.eu-west-2.amazonaws.com (NXDOMAIN)
vpn-internal.example.com: private address in public DNS: 10.0.4.20

Schedule it weekly and send the output to your security or operations team, so new issues are caught between full audits.

DNS Security Audit Checklist

AreaCheckGood result
InventoryEvery zone and record has an owner and purposeNo unexplained records
DelegationParent and zone NS match; serials agreeConsistent, all authoritative
DanglingCNAME, MX, NS targets exist and are yoursNo NXDOMAIN targets
IPsA and AAAA point at addresses you controlNo unknown or released IPs
LeaksNo private IPs or sensitive names in public zonesInternal names in internal DNS
EmailOne SPF, DMARC enforced, DKIM currentp=reject or p=quarantine
CertificatesCAA lists only your CAsCAA present
IntegrityDNSSEC enabled and validDS present, ad flag set
NameserversAXFR refused, no open recursionTransfer failed, REFUSED
Accounts2FA, least privilege, lock, auto-renewAll enabled

FAQ: DNS Security Audits

Do a full audit at least once or twice a year, and run automated checks weekly. Also review DNS whenever you decommission a service, change providers or offboard an agency or staff member with access.

Dangling records are among the most common and most serious. CNAMEs pointing at deleted cloud resources and A records pointing at released IP addresses can let attackers serve content on your subdomains.

Partly. You can check known hostnames, email records, CAA and DNSSEC from outside, and Certificate Transparency logs reveal many subdomains. But only the provider's export or API gives you the complete record list.

Usually, once the service has verified your domain, but some providers re-check periodically and will mark your domain unverified if the record disappears. Check with the service before removing a record for anything you still use.

Generally no. Private IP addresses and internal service names give attackers a map of your network. Keep them in internal DNS, using split-horizon DNS if the same name needs different answers inside and outside.

No. dig, a Certificate Transparency search, your provider's export and a short script cover the essentials. Paid attack surface management tools help when you have many domains and want continuous monitoring and alerting.


Conclusion

A DNS security audit is mostly about finding records that have outlived their purpose. Start with a complete inventory, then look for dangling CNAMEs, MX and NS records, A records pointing at IPs you no longer own, and anything that leaks internal details. Tighten email authentication, add CAA and DNSSEC, and make sure your nameservers refuse zone transfers and recursion.

Finish with the accounts, because two-factor authentication, registrar lock and tidy access lists protect everything else. Then automate the routine checks and repeat the full audit regularly, so your DNS stays as clean as the day you reviewed it.

Tags :
Share :

Related Posts

What is the difference between an A record and a CNAME record?

What is the difference between an A record and a CNAME record?

The difference between an A record and a CNAME record is what they point to. An A record points a hostname directly at an IPv4 address, such

Dive Deeper
What are the best DNS providers for websites?

What are the best DNS providers for websites?

The best DNS providers for most websites are Cloudflare for a fast, free, easy starting point, Amazon Route 53 if you already run on AWS, **G

Dive Deeper
What are the best public DNS resolvers?

What are the best public DNS resolvers?

The best public DNS resolvers for most people are Cloudflare (1.1.1.1) for speed and privacy, Google Public DNS (8.8.8.8) for reliability and

Dive Deeper