Share
Embed
Melinda Shore - Advanced DNS Services for Securing Your Application and Enhancing User Privacy
This talk introduces new features that have been added to the Domain Name System recently, and how to use those features to improve application security and user privacy. I also introduce the "getdns" Python library, which provides a simplified DNS API, and how to interface with popular crypto libraries.
https://us.pycon.org/2016/schedule/presentation/2004/
PyCon 2016
May 29, 2016
Transcript
31 slides
-
GETDNS • new DNS library • one-step DNSSEC validation •
transport options to protect user privacy, avoid middlebox woes -
GETDNS • Original specification was for a C API, actually
fits Python and other modern languages somewhat better (data structures) • Language bindings: Python, node.js, PHP. Ruby is under development. • Hackathons: TNW, IETF. Won “Best Internet Security Improvement” at IETF 94 hackathon in November -
PLUMBING HAPPENS • changes in network protocols can make application
developers’ lives easier • here’s how -
5-MINUTE DNS TUTORIAL • stateless query-response protocol • send a
query to your DNS server, it returns your query along with a set of answers • DNS resource records -
DNS STRUCTURE . .org .com .uk .biz .reisen ietf eff
python isoc mail pypi www root zone TLDs second-level domains -
DIG EXAMPLE Melindas-MacBook-Pro:~ melinda$ which dig /usr/bin/dig Melindas-MacBook-Pro:~ melinda$ dig
getdnsapi.net a ! ; <<>> DiG 9.8.3-P1 <<>> getdnsapi.net a ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1925 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ! ;; QUESTION SECTION: ;getdnsapi.net. IN A ! ;; ANSWER SECTION: getdnsapi.net. 449 IN A 185.49.141.37 ! ;; Query time: 244 msec ;; SERVER: 8.8.8.8#53(8.8.8.8) ;; WHEN: Thu May 26 20:06:01 2016 ;; MSG SIZE rcvd: 47 ! Melindas-MacBook-Pro:~ melinda$ -
DNSSEC • The problem: it’s easy to forge DNS packets,
with obvious consequences (AKA “Kaminsky attack,” after Dan Kaminsky) • DNSSEC is a mechanism to prove the authenticity of a DNS record • The trust model: hierarchy of signed records chaining up to the DNS root zone -
DNSSEC EXAMPLE Melindas-MacBook-Pro:src melinda$ dig +dnssec getdnsapi.net a ! ;
<<>> DiG 9.8.3-P1 <<>> +dnssec getdnsapi.net a ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27162 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ! ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 512 ;; QUESTION SECTION: ;getdnsapi.net. IN A ! ;; ANSWER SECTION: getdnsapi.net. 417 IN A 185.49.141.37 getdnsapi.net. 417 IN RRSIG A 7 2 450 20160608170833 20160518143030 23885 getdnsapi.net. bDcGGokWnupa9khd8rhr0SbjUEXHFmCpUWlbkNeXZx/Ugy90eWvpcY72 H2LWale/2CP5Q4V/+M0XMnEakkZOFBA3h58n/8pGK3MuSHthX/ E0CD1b DFvCgfeLxyFde5RoIpZ6Mx0SVG5/3A/Lc2Yn56MUcBecLKHBNLqv+oux /Ys= ! ;; Query time: 133 msec ;; SERVER: 8.8.8.8#53(8.8.8.8) ;; WHEN: Thu May 26 21:47:11 2016 ;; MSG SIZE rcvd: 231 -
ENTER GETDNS shore@birch:~/src/tmp$ cat simple.py import getdns, sys ! context
= getdns.Context() ext = { 'dnssec_return_only_secure': getdns.EXTENSION_TRUE } r = context.address(sys.argv[1], extensions=ext) if r.status == getdns.RESPSTATUS_GOOD: for addr in r.just_address_answers: print(addr['address_data']) if r.status == getdns.RESPSTATUS_NO_SECURE_ANSWERS: print("No secure answers”) ! shore@birch:~/src/tmp$ python simple.py getdnsapi.net 2a04:b900:0:100::37 185.49.141.37 ! shore@birch:~/src/tmp$ python simple.py google.com No secure answers shore@birch:~/src/tmp$ -
A NEW TRUST MODEL FOR THE INTERNET Hey, this thing
securely serves up public keys! -
PKI • You’ve got to trust somebody • the public
key infrastructure is also based on a hierarchy of credentials chaining back to a root • browser vendors end up making decisions about what goes in the root trust store • That hasn’t always worked out well -
PKI ISSUES • certificate “misissuance” • careless key usage constraints
— people posing as CAs and issuing certs • compromised CAs -
DANE • DNS-Based Authentication of Named Entities • Here’s the
idea: move trust management and credential validation closer to an organization’s own infrastructure • Here’s the implementation: put public key credentials in the DNS, protected by DNSSEC -
DANE • to authenticate TLS servers, retrieve a TLSA record
from the DNS • make sure its signature checks out • compare the certificate in the TLSA record with the one presented in the TLS server_hello -
RETRIEVE THE TLSA RECORD ctx = getdns.Context() try: results =
ctx.general(name=qname, request_type=getdns.RRTYPE_TLSA, extensions=extensions) -
GET THE SERVER CERT connection = SSL.Connection(sslctx, sock=sock) connection.connect((ipaddr, port))
chain = connection.get_peer_cert_chain() cert = chain[0] -
PULL DATA OUT OF THE RECORD def get_tlsa_rdata_set(replies, requested_usage=None): tlsa_rdata_set
= [] for reply in replies: for rr in reply['answer']: if rr['type'] == getdns.RRTYPE_TLSA: rdata = rr['rdata'] usage = rdata['certificate_usage'] selector = rdata['selector'] matching_type = rdata['matching_type'] cadata = rdata['certificate_association_data'] cadata = str(cadata).encode('hex') if usage == requested_usage: tlsa_rdata_set.append( (usage, selector, matching_type, cadata) ) return tlsa_rdata_set -
COMPARE WHAT YOU’VE GOT def verify_tlsa(cert, usage, selector, matchtype, hexdata1):
! if selector == 0: certdata = cert.as_der() elif selector == 1: certdata = cert.get_pubkey().as_der() else: raise ValueError("selector type %d not recognized" % selector) ! if matchtype == 0: hexdata2 = hexdump(certdata) elif matchtype == 1: hexdata2 = compute_hash(hashlib.sha256, certdata) elif matchtype == 2: hexdata2 = compute_hash(hashlib.sha512, certdata) else: raise ValueError("matchtype %d not recognized" % matchtype) ! if hexdata1 == hexdata2: return True else: return False -
OTHER DANE APPLICATIONS • openpgp keys • S/MIME keys •
use of TLSA records to protect SMTP sessions -
ENCRYPTION EXAMPLE • https://raw.githubusercontent.com/getdnsapi/ getdns-python-bindings/master/examples/ dane_encrypt.py • This was written
quite early in the DANE process, and the S/MIME key was stored as a TLSA record -
DNS PRIVACY • IETF RFC 7258: “Pervasive monitoring is a
technical attack that should be mitigated in the design of IETF protocols, where possible.” • DNS leaks a massive amount of information about what a user is doing on the network -
PRIVACY PROTECTION • TLS transport: context.dns_transport_list = [ getdns.TRANSPORT_TLS
] • Padding: context.tls_query_padding_blocksize = 256 -
“ROADBLOCK” AVOIDANCE • Middleboxes (firewalls, NATs, other stateful transport intermediaries)
sometimes filter out DNS traffic it misidentifies as malicious • The underlying getdns library detects these and works around them -
STATUS • Now feature-complete with respect to the original spec
• Ongoing integration of new protocol features • Python bindings have been very useful for quick prototyping -
FIND US! • Project home page: https://getdnsapi.net • Github: https://github.com/getdnsapi
• PyPI: https://pypi.python.org/pypi/getdns/v1.0.0b1 • Documentation: http://getdns.readthedocs.org/ • Docker image: https://hub.docker.com/r/melindashore/ getdns-python2/ -
UPCOMING HACKATHON • IETF 96 Hackathon, Berlin, Germany • Intercontinental
Hotel, July 16/17, 2016 • You do not need to be registered for or participating in the IETF meeting • Potential projects include: a getdns protocol for Twisted, a DANE API, or your excellent idea • http://ietf.org/hackathon/96-hackathon.html