DNSSEC with NSEC3

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 an introduction of DNSSEC with BIND refer to this blog post or note the DNSSEC category on my blog.) For now I am using another test domain:
sshfp.net . I will also show some dig commands for verifying the NSEC3 hashes.

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 permissions (refer to my lasts posts):

1

2

3

4

5

cd/etc/bind/keys/

sudo dnssec-keygen-aRSASHA512-b2048-3-fKSK-r/dev/urandom sshfp.net

sudo dnssec-keygen-aRSASHA512-b2048-3-r/dev/urandom sshfp.net

sudo chmodg+r*.private

sudo rndc loadkeys sshfp.net

(I also tried the algorithm number 14, 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 : “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.

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”:

1

sudo rndc signing-nsec3param10105053851Bsshfp.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 …):

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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

weberjoh@jw-nb12-lx:~$dig sshfp.netnsec3param

;<<>>DiG9.10.3-P4-Ubuntu<<>>sshfp.netnsec3param

;;global options:+cmd

;;Got answer:

;;->>HEADER<<-opcode:QUERY,status:NOERROR,id:48904

;;flags:qr rd ra;QUERY:1,ANSWER:1,AUTHORITY:3,ADDITIONAL:4

;;OPT PSEUDOSECTION:

;EDNS:version:0,flags:;udp:4096

;;QUESTION SECTION:

;sshfp.net.INNSEC3PARAM

;;ANSWER SECTION:

sshfp.net.0INNSEC3PARAM102080637D8AF055B5EECA2A621EDAAA3C5E

;;AUTHORITY SECTION:

sshfp.net.172800INNS ns1.weberdns.de.

sshfp.net.172800INNS ns3.weberdns.de.

sshfp.net.172800INNS ns2.weberdns.de.

;;ADDITIONAL SECTION:

ns1.weberdns.de.86400INA80.154.108.230

ns1.weberdns.de.86400INAAAA2003:51:6012:110::a07:53

ns2.weberdns.de.86400INA213.61.29.182

;;Query time:150msec

;;SERVER:192.168.120.22#53(192.168.120.22)

;;WHEN:Mon Nov1411:20:00CET2016

;;MSG SIZE rcvd:196

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 😉 :