By default DNSSEC uses the next secure (NSEC) resource record “to provide authenticated denial of existence for DNS data”, RFC 4034. This feature creates a complete chain of all resource records of a complete zone. While it has its usage to prove that no entry exists between two other entries, it can be used to “walk” through a complete zone, known as zone enumeration. That is: an attacker can easily gather all information about a complete zone by just using the designed features of DNSSEC.

For this reason NSEC3 was introduced: It constructs a chain of hashed and not of plain text resource records (RFC 5155). With NSEC3 enabled it is not feasible anymore to enumerate the zone. The standard uses a hash function and adds the NSEC3PARAM resource record to the zone which provides some details such as the salt.

In this post I will show how to create the KSK and ZSK keys that are NSEC3-capable and how to insert the NSEC3PARAM parameters to the zone. For now I am using another test domain: sshfp.net . I will also show some dig commands for verifying the NSEC3 hashes.

For an introduction of DNSSEC with BIND refer to this blog post or note the DNS overview page on my blog.

NSEC3-Capable Algorithm

The first step to sign the zone is the creation of appropriate keys. The command line interface tool dnssec-keygen  provides the -3  option: “Use an NSEC3-capable algorithm to generate a DNSSEC key. If this option is used and no algorithm is explicitly set on the command line, NSEC3RSASHA1 will be used by default. Note that RSASHA256, RSASHA512, ECCGOST, ECDSAP256SHA256 and ECDSAP384SHA384 algorithms are NSEC3-capable.” I decided to use the RSASHA512 algorithm, so here are my commands for the generation of the KSK and the ZSK, as well as the correct ownerships (refer to my lasts posts):

(I also tried the algorithms 13 and 14, ECDSAP256SHA256 and ECDSAP384SHA384, but this did not work at all. rndc was not responding anymore. Maybe this is related to a bug in my BIND version?)

Note that up to this point the mere NSEC is still used for signing the zone. To enable NSEC3, follow the next step:

Adding the Salt

To tell the zone to use NSEC3, the NSEC3PARAM resource record must be inserted. This is done with rndc signing -nsec3param along with many parameters: “rndc signing -nsec3param sets the NSEC3 parameters for a zone. […] Parameters are specified in the same format as an NSEC3PARAM resource record: hash algorithm, flags, iterations, and salt, in that order.”

There is only one registered hash function that is used for NSEC3, namely SHA1, refer to DNSSEC NSEC3 Hash Algorithms. (Why aren’t there any better hash functions yet? Short discussion here.) SHA1 has value number 1 listed within the nsec3param section. The flags parameter is left by 0. For the hash iterations I chose 10. (Some notes are here.) This sets the cost of computing a dictionary for the attacker, as well as the cost for signing the zone. To my mind 10 is a good starting point but it could be increased if needed.

"Kosher Salt" by stlbites.com is licensed under CC BY-ND 2.0
Kosher Salt” by stlbites.com is licensed under CC BY-ND 2.0

Finally, a salt must be set to prevent precomputed dictionary attacks. Choose a random salt, e.g., by using a password generator. Some other tutorials recommend a salt length of 8 hexadecimal digits, but it can be much longer. However, I used 8 digits, too. (“The salt SHOULD be at least 64 bits long and unpredictable, so that an attacker cannot anticipate the value of the salt and compute the next set of dictionaries before the zone is published”, RFC 5155 section 12.1.1.)


In the end this is my rndc command to use NSEC3 for my zone called “sshfp.net”:

Without any other commands BIND is now using NSEC3 with the just defined parameters.

Test with Dig

The first step to test is the presence of the nsec3param record. Answer in line 15:

The more interesting test case is querying a non-existent domain. With mere NSEC, some plain text FQDNs would be revealed. Not so with NSEC3 which shows many (and long …) hashes, as well as the DNSSEC signatures (which are even longer in my case since I used the SHA512 hash for signing …):

In fact, no other DNS leaves are revealed with this query. Great!

Changing the Salt

“The salt SHOULD be changed periodically to prevent pre-computation using a single salt”, RFC 5155 section C.1.

Ok, so let’s change the salt exemplary. This is really simple because it only requires the single rndc command I already showed. For this example I increased the size of the salt to 32 bytes (128 bits) as well as the hash iteration from 10 to 20:

Without any problems BIND signed the complete zone with this new parameters. Here is the new nsec3param answer:

And the NSEC3 answer for a non-existent domain which also shows the new salt, etc. Note that some NXDOMAIN answeres have 3 different NSEC3 records, refer to RFC 7129, section 5 (a litte bit complicated though). Furthermore note that for the following listing I wrapped the lines in the output to see the whole big bunch of the answer ;) :

That’s it. ;) Good luck.


Featured image: “Chain” by Astro is licensed under CC BY 2.0.

7 thoughts on “DNSSEC with NSEC3

  1. I too tried ECDSAP256SHA256 in March 2019 and it appears that something breaks in BIND/rndc or other. It is buggy…. would result in ‘file not found’ error in syslog.

    So I’ve defaulted to RSASHA256. I will try RSASHA512 at a later date.

    Thanks for the informative website!

  2. Thank you for some wonderfully clear and well-presented DNSSEC info!

    If I might ask, after much reading I’m still not clear on this: Does BIND retain the NSEC3 parameter, and if so, where? i.e. Is it retained over BIND restarts? What rndc commands would cause the NSEC3 to be lost?


  3. Hi,

    I still can’t understand what purpose the NSEC3PARAM resource record serves. If I run an authoritative DNS server, does it use its own NSEC3PARAM resource record to create its own NSEC3 resource records?

    kind regards

Leave a Reply

Your email address will not be published. Required fields are marked *