An unauthorised DNS change is one of the least visible problems a site can have, because it happens entirely outside your server.
Why it goes unnoticed
Changing a record touches nothing you control. The server keeps running, its logs show normal traffic from whoever still reaches it, and every check on the machine reports health.
Meanwhile visitors are directed somewhere else.
Worse, anyone whose resolver still holds the old record sees the correct site, and that frequently includes you, because your own machine has the answer cached. The problem is invisible from the perspective most likely to look at it.
The mail record is the damaging one
Redirecting the website is noticed reasonably quickly, because visitors say so.
Redirecting mail is not. Messages can be received, read, and forwarded on to the real destination, so nothing appears missing at all, while every password reset, invoice and confidential exchange passes through somebody else.
That is the version worth monitoring for specifically.
Record what the records should be
Monitoring starts with knowing the correct answer. Capture it:
for t in A AAAA MX NS TXT; do echo "== $t"; dig example.com $t +short; done
Keep that output somewhere. It is also what you would need to rebuild the zone if the DNS provider disappeared. For that scenario, see secondary DNS and what happens when DNS fails.
Compare from outside, regularly
A simple check run from somewhere other than your own network:
#!/bin/sh EXPECTED_A="203.0.113.10" EXPECTED_MX="10 mail.provider.com." A=$(dig @1.1.1.1 example.com A +short) MX=$(dig @1.1.1.1 example.com MX +short) [ "$A" = "$EXPECTED_A" ] || echo "A record changed: $A" [ "$MX" = "$EXPECTED_MX" ] || echo "MX record changed: $MX"
Run it on a schedule and have it alert rather than log. Querying a public resolver rather than your own is deliberate. It shows what the world sees.
Include the nameserver records themselves. A change at the registrar redirects the entire zone at once, and that is the most complete form of the problem.
Watch the certificate too
A hijack that serves a working HTTPS site requires a certificate, and obtaining one for a domain you now control is easy.
Certificate transparency logs record every certificate issued for a domain publicly, and monitoring services will alert on new ones. A certificate issued for your domain that you did not request is a strong signal, and often the earliest one available.
Why a certificate is trusted goes into why the padlock does not help the visitor here.
The actual defence
DNS itself is rarely attacked. What is attacked is the account that can change it.
Two-factor authentication on the registrar and the DNS provider. Not optional for a domain a business depends on.
The registrar's transfer lock on, so the whole domain cannot be moved. Domain locking picks it up from there.
A contact address that is monitored and not on the domain itself, so change notifications actually arrive.
Few people with access, reviewed when staff change.
If it happens
Change the account passwords first, from a machine you trust, then correct the records. Correcting them while the attacker still has access simply invites a second change.
Then check what else the compromised account controls: a registrar login usually holds several domains.
Expect recovery to be slower than the attack: caches hold the wrong answer for the length of the TTL, and there is no way to expire them early. A shorter TTL limits the damage window, which is one of the few arguments for keeping TTLs modest rather than long. Understanding TTL explains the trade against resilience.
Finally, treat every password that could have been reset by email during the window as compromised, because mail may have been redirected. What to do when an email account is compromised sets out that sequence.
Alert on the change, not on a schedule you read
A check that writes to a file nobody opens is not monitoring. The difference is where the result goes.
#!/bin/sh STATE=/var/lib/dnswatch/example.com NEW=$(dig @1.1.1.1 example.com A MX NS +short | sort | sha256sum | cut -c1-16) OLD=$(cat "$STATE" 2>/dev/null) if [ "$NEW" != "$OLD" ]; then printf 'DNS changed for example.com\n\n' | mail -s "DNS change" [email protected] echo "$NEW" > "$STATE" fi
Hashing the combined records means one comparison covers every record type, and the alert fires on any difference including ones you made deliberately, which is correct, because a change you expected is confirmed in a second and one you did not is the whole point.
Send it to an address that does not depend on the domain being watched. An alert about DNS failing, delivered by mail that DNS failure prevents, is not an alert.
Check the delegation, not only the records
The most complete hijack changes the nameservers at the registrar, at which point your own nameservers are simply no longer asked, and a check that queries them directly sees nothing wrong.
dig example.com NS +short dig example.com NS @a.gtld-servers.net +short
The second asks the parent zone what it publishes, which is what the world actually follows. A difference between the two is the signal, and it is invisible to any check that only compares record values.
Include it in whatever runs on a schedule. It is the case that matters most and the one most monitoring omits. Using dig and nslookup walks through reading the answers.
Watch the registrar account, not just the zone
Since the attack is nearly always on the account rather than on DNS, the account's own signals are worth as much as the record checks.
Enable notifications for logins, contact changes and nameserver changes where the registrar offers them. Review who has access after any staff change. And confirm the account's own contact address is one you monitor and is not on a domain held in that account.
That last point closes a specific circularity: a hijack that changes the nameservers also breaks delivery of the notification about it, if that notification goes to the affected domain. Managing a portfolio of domains explains the address.
Know what the correct answer is
Detection requires a reference, and reconstructing one during an incident is not possible.
for t in A AAAA MX NS TXT CAA; do echo "; $t"; dig example.com $t +short; done > dns-baseline.txt
Keep that file with the domain's other records, updated deliberately whenever something changes legitimately.
It serves twice: as the comparison for monitoring, and as the reconstruction list if the zone is ever lost or has to be rebuilt at a different provider. For that scenario, see secondary DNS and what happens when DNS fails.