The Nessus finding DNSSEC NSEC Records (plugin 59959) means your signed zone answers negative queries with NSEC records. Those records link real hostnames in order, which allows DNSSEC NSEC zone walking: anyone can list every name in the zone. Fix it by re-signing the zone with NSEC3, using 0 extra iterations, an empty salt and no opt-out (RFC 9276).
What the scanner is actually detecting
When a DNSSEC-signed zone has to prove that a name does not exist, it returns a signed NSEC record naming the previous and the next existing name in sorted order. Query a name just after the “next” name and you get the following one. Repeat until the chain loops back to the apex and you have the whole zone, without a zone transfer.
- Nessus plugin 59959, “DNSSEC NSEC Records”: family DNS, severity Medium, CVSS v2 base score 5.0. It builds on an earlier Nessus DNSSEC check (dnssec_resolver.nasl) that finds a DNSSEC server on UDP 53 and the zone it answers for, and it fires when that server’s negative answers for the zone use NSEC. Tenable’s solution: remove NSEC records for the affected zones and sign with NSEC3.
NSEC3 replaces each name with a SHA-1 hash of the name, so the chain links hashes instead of hostnames. The zone can still prove non-existence, but a walker collects hashes, not names.
Real-world risk, stated honestly
This is information disclosure, not a compromise path. Zone walking reveals the same names a zone transfer would: staging hosts, VPN portals, forgotten records. The records themselves are public by design, and nothing can be changed or read beyond DNS data.
Keep two facts in mind. First, NSEC3 only raises the cost. RFC 9276 notes that an attacker can collect the hashes and crack guessable names (www, mail, vpn) offline, and many hostnames already leak through certificate transparency logs and other sources; a passive DNS subdomain inventory shows what outsiders can already see. Second, RFC 9276 says NSEC should be preferred when NSEC3’s features are not needed, and ISC’s DNSSEC guide calls NSEC a good choice in most cases. For a zone of purely public names, accepting the finding is defensible. For zones with internal-looking names, switching is cheap.
How to confirm it on the host
Query the authoritative server directly (replace the address and zone with your own):
# The apex NSEC record names the next hostname in the zone
dig +short example.com NSEC @203.0.113.53
# Empty output here means the zone uses NSEC, not NSEC3
dig +short example.com NSEC3PARAM @203.0.113.53
# Look at the AUTHORITY section: NSEC or NSEC3?
dig +dnssec +multiline no-such-name-4815.example.com A @203.0.113.53
To see what an attacker sees, walk the zone with ldns-walk (package ldnsutils) or the Nmap script that ships with Nmap:
ldns-walk @203.0.113.53 example.com
nmap -sSU -p 53 --script dns-nsec-enum --script-args dns-nsec-enum.domains=example.com 203.0.113.53
Before changing anything, check the signing algorithm. The third field of each DNSKEY is the algorithm number; 5 is RSASHA1, which cannot use NSEC3 directly.
dig +short example.com DNSKEY @203.0.113.53 | awk '{print $1, $3}'
How to fix it
BIND 9.18 and later with dnssec-policy
The nsec3param statement inside a dnssec-policy block switches the zone from NSEC to NSEC3. The built-in default policy uses NSEC, so zones on dnssec-policy default need a named policy. Copy the default’s key settings (one CSK, ECDSAP256SHA256, unlimited lifetime; named -C prints it) so the key does not change:
dnssec-policy "default-nsec3" {
keys {
csk lifetime unlimited algorithm ecdsap256sha256;
};
nsec3param iterations 0 optout no salt-length 0;
};
zone "example.com" {
type primary;
file "example.com.db";
dnssec-policy "default-nsec3";
};
If the zone already uses a custom policy, add only the nsec3param line to it. Record the key state, apply, then compare:
rndc dnssec -status example.com
named-checkconf
rndc reconfig
The log should show zone_addnsec3chain(1,CREATE,0,-) while named builds the new chain, and the key tag in rndc dnssec -status should be unchanged. A bare nsec3param; gives the same 0/no/0 values, and BIND 9.20 requires iterations 0 for dnssec-policy signing.
BIND with auto-dnssec or offline signing
For inline-signing zones still on the deprecated auto-dnssec maintain option, set the parameters at runtime (algorithm 1, flags 0, 0 iterations, no salt, which is the only configuration ISC recommends):
rndc signing -nsec3param 1 0 0 - example.com
For zones signed offline with dnssec-signzone, re-sign with an NSEC3 chain and no salt:
dnssec-signzone -S -3 - -H 0 -o example.com example.com.db
Windows Server DNS
Windows Server 2012 and later support NSEC3, and the zone signing wizard defaults to it, so an NSEC zone was usually a deliberate choice. Make the change on the Key Master. In DNS Manager, right-click the zone, point to DNSSEC, click Properties, open the Next Secure (NSEC) tab, select Use NSEC3, set Iterations to 0 and click OK. Microsoft documents that editing these parameters re-signs the zone once you confirm. In PowerShell:
Get-DnsServerSigningKey -ZoneName example.com | Select-Object KeyType, CryptoAlgorithm
Set-DnsServerDnsSecZoneSetting -ZoneName example.com -DenialOfExistence NSec3 -NSec3Iterations 0 -NSec3OptOut $false
Invoke-DnsServerZoneSign -ZoneName example.com -DoResign
Windows defaults to 50 iterations and an 8-octet random salt, so set iterations explicitly. RFC 9276 also recommends no salt; the iteration count matters far more, so if your version will not accept an empty salt, keep the default. Microsoft notes that NSEC3 cannot be combined with RSA/SHA-1 keys.
PowerDNS Authoritative
PowerDNS signs online and uses NSEC by default. Called with only the zone name, set-nsec3 defaults to ‘1 0 0 -‘:
# PowerDNS 5.0 and later
pdnsutil zone set-nsec3 example.com '1 0 0 -'
pdnsutil zone rectify example.com
# Before 5.0
pdnsutil set-nsec3 example.com '1 0 0 -'
pdnsutil rectify-zone example.com
Rectifying recalculates the stored ordering fields to match the new setting. Do not add the narrow option if the zone has secondaries: narrow mode signs “white lies” on the fly and PowerDNS then refuses AXFR for the zone.
How to verify the fix and rescan
- Run dig +short example.com NSEC3PARAM @server against every authoritative server in the NS set. Expect 1 0 0 – (Windows may show a salt).
- Repeat the non-existent name query. The AUTHORITY section should contain NSEC3 records, and ldns-walk and dns-nsec-enum should no longer list names.
- Check that validation still works: delv no-such-name-4815.example.com A should return a validated negative answer, not “resolution failed”.
- Compare SOA serials across secondaries, then rescan the same targets with plugin 59959 enabled.
What can break and how to roll back
- RSASHA1 zones (algorithm 5). ISC documents that moving to NSEC3 requires an algorithm rollover to algorithm 7, and PowerDNS warns that switching zones on algorithm 5 or 7 needs a DS update at the parent. Zones on current algorithms such as 8 (RSASHA256) or 13 (ECDSAP256SHA256) keep their keys and DS.
- Non-zero iterations. RFC 9276 notes that extra iterations raise CPU cost and interoperability risk, and that resolvers may treat such zones as insecure. BIND 9.20 treats more than 50 iterations as insecure, so the Windows default sits at that limit.
- Opt-out. It only helps huge zones of unsigned delegations and weakens the proof of non-existence. Leave it off.
- PowerDNS narrow mode stops zone transfers to secondaries.
Rollback is symmetrical. BIND: remove the nsec3param line (or point the zone back at its previous policy) and run rndc reconfig; for auto-dnssec zones run rndc signing -nsec3param none example.com. Windows: Set-DnsServerDnsSecZoneSetting -ZoneName example.com -DenialOfExistence NSec, then re-sign. PowerDNS: pdnsutil zone unset-nsec3 example.com (pdnsutil unset-nsec3 before 5.0).
Common false positive reasons
- The host is a resolver, not the authority. Check the zone named in the plugin output. If dig @host zone SOA returns no aa flag, the NSEC records belong to someone else’s zone.
- One server still serves NSEC. A secondary with a stale copy, an anycast node or a second signer was missed.
- Synthetic NSEC. Online signers that return minimally covering NSEC records (RFC 4470) cannot be walked, but a check that looks for NSEC can still flag them. Try ldns-walk before changing anything.
- NSEC by choice. The zone holds only public names and NSEC is deliberate. Record an accepted risk rather than a false positive.
FAQ
Does NSEC3 make my hostnames secret?
No. It stops trivial enumeration, but hashes of guessable names can be cracked offline. Keep truly internal names out of public DNS, for example with split DNS.
What NSEC3 parameters should I use?
RFC 9276 recommends 1 0 0 –: SHA-1, no opt-out, 0 extra iterations, empty salt. Extra iterations and salt add cost without meaningful protection.
Do I need to update the DS record at my registrar?
Not if the keys stay the same. NSEC3 changes the denial records, not the DNSKEY. Zones on the old RSASHA1 algorithms are the exception, because the switch involves new keys and therefore a new DS.
Is Nessus 59959 urgent?
It is rated Medium (CVSS v2 5.0) and discloses names only. Fix it in a planned change window, starting with zones that contain internal hostnames.
Tracking this finding across many hosts
Plugin 59959 is reported per DNS server, so one zone served by several name servers yields several findings, and one stale secondary can keep the finding open. SITEY, a self-hosted vulnerability management platform, imports Nessus results when you upload the exported .nessus file (they are not pulled automatically), merges duplicates per scanner and can retest an individual Nessus finding to confirm it has closed.
Sources
- Tenable: Nessus plugin 59959, DNSSEC NSEC Records
- RFC 9276: Guidance for NSEC3 Parameter Settings
- ISC BIND 9 DNSSEC Guide: NSEC and NSEC3, migrating from NSEC to NSEC3
- Microsoft Learn: DNS Zones (DNSSEC signing options, NSEC3 defaults, DNSSEC properties)
- PowerDNS Authoritative: DNSSEC operational instructions (set-nsec3)