DNSSEC is a more secure method for using DNS, and involves signing DNS responses with key pairs. In this case we will use RSA keys to sign a DNSSEC response [article]:
DNSSEC |
Outline
Did you know that the whole of the Internet could crash in an instance … just because of one protocol? … DNS. A massive distributed denial of service (DDoS) against the core DNS infrastructure would cripple most of the Internet, as we are so dependent on DNS servers to resolve IP addresses. If the core fails to work, the rest of the infrastructure would collapse. And, that, at any time, our DNS infrastructure could be taken-over for malicious purposes?
DNS is a terrible protocol. It relies on one DNS server telling others what the IP resolution of the domain name is. But who can you trust to seed this? Well, the main authority of the domain. We don’t have to integrate the main authority as the information on the domains will be propagated through the Internet. So what happens if a malicious entity sees the wrong IP address? Let’s say a nation state — MegaTropolis — wants to take over a little state — MiniTropolis. Well, their first focus might be poisoning DNS caches around the world, so that all the MiniTopolis domains pointed to MegaTropolis sites.
The problem with DNS is that it has virtually no real security built into it, and where a fake DNS system can easily be setup and redirect users to incorrect sites. But there is a solution … Domain Name System Security Extensions (DNSSEC). It provides origin authentication of DNS data, along with data integrity. It does not stop someone from viewing the data in the request and reply. The issues related to DNS have been known for a while:
The threats include: Packet interception; ID Guessing and Query Prediction; Name Chaining; Betrayal By Trusted Server; Denial of Service; Authenticated Denial of Domain Names; and Wildcards. One of the greatest threats is DNS cache poisoning, and where a malicious host can seed an incorrect domain name for the rest of the network. DNSSEC overcomes this by having a protected zone in which all the responses are digitally signed. DNS resolves can then check that the DNS information has been signed by one of the trusted hosts.
DNS works by creating a domain record which defines an SOA (Start of Authority). This then defines the serial number, the refresh time, and so on:
Within this we can define an NS (Name Server) and MX (Mail Server), along with the IP addresses of defined hosts within the domain. We can use nslookup to interrogate the entry:
It should be noted the DNSSEC does not provide confidentially of the data, or does it protect against a denial of service attack.
Coding
With this we will create an 2,048 bit RSA key pair (a public key and a private key), and then sign the SOA with the private key of a trusted domain. Others can then check the signature using the public key of the trusted domain. The SOA entry is created as in DNS, with a response header for the domain name, and the IP addresses (dns.ClassINET):
The serial number is important on the SOA, as it defines the most up-to-date version of the entry. In this case the signature created with an RSA encrypted SHA-256 hash. We also include a date for the expiry time for the signature. In this case the signature is signed the entity defined in the key (key.Hdr.Name):
package main import ( "crypto" "crypto/rsa" "github.com/miekg/dns" "fmt" "os" ) func main() { domain:="asecuritysite.com" argCount := len(os.Args[1:]) if (argCount>0) {domain= string(os.Args[1])} domain = domain+"." key := new(dns.DNSKEY) key.Hdr.Name = domain key.Hdr.Rrtype = dns.TypeDNSKEY key.Hdr.Class = dns.ClassINET key.Hdr.Ttl = 3600 key.Flags = 256 key.Protocol = 3 key.Algorithm = dns.RSASHA256 priv, _ := key.Generate(2048) soa := new(dns.SOA) soa.Hdr = dns.RR_Header{domain, dns.TypeSOA,dns.ClassINET, 14400, 0} soa.Ns = "ns."+domain soa.Mbox = "mail."+domain soa.Serial = 1293945905 soa.Refresh = 14400 soa.Retry = 3600 soa.Expire = 604800 soa.Minttl = 86400 sig := new(dns.RRSIG) sig.Hdr = dns.RR_Header{domain, dns.TypeRRSIG, dns.ClassINET, 14400, 0} sig.TypeCovered = dns.TypeSOA sig.Algorithm = dns.RSASHA256 sig.Labels = 2 sig.Expiration = 1562761057 sig.Inception = 1562761057 sig.OrigTtl = soa.Hdr.Ttl sig.KeyTag = key.KeyTag() sig.SignerName = key.Hdr.Name var pr crypto.Signer pr,_= newSignerFromKey(priv) if err := sig.Sign(pr, []dns.RR{soa}); err != nil { fmt.Printf("Failed to sign") return } if err := sig.Verify(key, []dns.RR{soa}); err != nil { fmt.Printf("Failed to verify") } else { fmt.Printf("Signature okay\n\n") } fmt.Printf("SOA: %s\n\n",soa) fmt.Printf("Sig: %s\n\n",sig) fmt.Printf("Key: %s\n\n",key) } type rsaPrivateKey struct { *rsa.PrivateKey } func newSignerFromKey(k interface{}) (crypto.Signer, error) { var sshKey crypto.Signer switch t := k.(type) { case *rsa.PrivateKey: sshKey = &rsaPrivateKey{t} default: return nil, fmt.Errorf("ssh: unsupported key type %T", k) } return sshKey, nil }
A sample run for a message of “abc.com.” is:
Signature okay SOA: abc.com. 14400 IN SOA ns.abc.com. mail.abc.com. 1293945905 14400 3600 604800 86400 Sig: abc.com. 14400 IN RRSIG SOA 8 2 14400 20190710121737 20190710121737 42981 abc.com. ofcc7BjeL/V7jItwDF39pCrvc m4FRRc7JQ9P4AFyVkVCQhAdwZ7RQ8fbc3D2u9GJZhBF3ZHWL4TY5Bj98noDYENk9GPSqK8jtY9bikv7wcg0ByoxOqMY7gxTgcl9SiEpzXWzV5tVj6dN27dGe AhfwhqvbBxKZ/1zziDzOF6DoqVEcItDrSAJyTI/iejnBsD4jXC4fKvBKIj3kGs967kNrHYKF0vcj+ST42rFCAXRoWZNAXHDhck84XO9K07XQncoIbf1u67KO Kv5xl82M0rUo1+InZ8DNrnD8+nsvY0yM217QSEMoiaqfo5CffkshafVBtElNXbncdOf7ycISd1iyQ== Key: abc.com. 3600 IN DNSKEY 256 3 8 AwEAAcKRvWqgs/ohPPTG/uUi8ZkaH8J+iDnlBwjhUXJtPXplbsxYzyBtLEfOyVOcbKpMmZN1MosH nP/T4gbTweIeXa9cIDsAuy+3SXlj+RNYozlQm8+LJofUIn7/LmNmLpKrRBGZpWZ4nqymDBUSsF5EJzDWPiiJFxDWA/8s8EVw8taBg0yaoMDkOjKfnWY5IHQG G8bRPP+yXa5AU+90Q3WYHOYqY68oF3M6U5n0QZhpr4C8lg3wOTwJyz4ubQiGfC1EzOdHV9jHBgtWuG9ohfgRYAy+NpfJkzdX7iPJ6J1mlZiUbodc84PnFY+P IwjI72XUQrAJbUTW+6Y5OPRchMlEoSU=