TLSA: DANE certificate pinning
The certificate-pinning version of SSHFP, but for TLS. TLSA records, served from a DNSSEC-signed zone, let a client verify that the cert offered by a TLS server is the one the domain owner authorized: no CA needed.
| Type number | 52 |
| RFC | 6698, updated by 7671 (operational guidance) and 7218 (the acronyms below) |
| RDATA | usage · selector · matching type · cert-association data |
| Required for trust | DNSSEC: without it TLSA is unreliable |
What it holds#
Four fields:
| Field | Codes |
|---|---|
| Usage | 0 = PKIX-TA · 1 = PKIX-EE · 2 = DANE-TA · 3 = DANE-EE |
| Selector | 0 = full cert · 1 = SubjectPublicKeyInfo |
| Matching type | 0 = exact · 1 = SHA-256 · 2 = SHA-512 |
| Cert association | Hex blob of the hash (with matching=0, the entire selected content: full cert or SPKI per the selector) |
Most common combo for SMTP-DANE (the only big DANE use case in practice), and the one RFC 7672 section 3.1 recommends:
_25._tcp.mail.example.com. TLSA 3 1 1 abc123...Usage 3, selector 1, matching type 1: DANE-EE with a SubjectPublicKeyInfo SHA-256 hash.
Name structure:
_<port>._<proto>.<host>. TLSA <usage> <selector> <matching> <hash>_25._tcp.mail.example.com pins the cert presented by mail.example.com:25/tcp. The underscore-prefixed naming convention is shared with SRV records and exists specifically to prevent collisions with real hostnames.
When to use it#
- SMTP servers: DANE for SMTP (RFC 7672) is the only mainstream production use. The receiving domain publishes TLSA records for its MX hosts; sending MTAs that support DANE (Postfix, Exim, Microsoft Exchange Online) validate the TLSA against the certificate the MX presents before handing over the mail. See email records for the full picture of mail-side hardening.
- Specialized internal services where you want to pin certs without operating a private CA.
When not to use it#
- For browser HTTPS. Browsers do not validate DANE. (The dream of "TLS without CAs" via TLSA-for-HTTPS quietly died.)
- If you cannot sign your zone. TLSA without DNSSEC is worse than no TLSA.
- If your cert rotates frequently and you cannot keep TLSA in sync: a stale TLSA is a hard outage.
dig example#
dig @1.1.1.1 _443._tcp.tlsa.example.isitdns.net TLSA +short3 1 1 290B4C3FDBE4C7601988C018FA65B7DBD436FA53834A2D7A5FD29551 99CFE140The space inside the hash (...29551 99CFE140) is cosmetic: dig folds long hex into chunks for display. Strip whitespace before comparing hashes.
nslookup prints the same record under a generic rdata_52 label (nslookup does not know TLSA by name):
nslookup -type=TLSA _443._tcp.tlsa.example.isitdns.net 1.1.1.1Server: 1.1.1.1
Address: 1.1.1.1#53
Non-authoritative answer:
_443._tcp.tlsa.example.isitdns.net rdata_52 = 3 1 1 290B4C3FDBE4C7601988C018FA65B7DBD436FA53834A2D7A5FD29551 99CFE140
Authoritative answers can be found from:nslookup cannot show the DNSSEC AD bit or RRSIG records. Use dig or curl DoH to verify the chain is intact.
That record is live in the demo zone: usage 3, selector 1, matching 1 (DANE-EE / SubjectPublicKeyInfo / SHA-256). The hash is a documentation sample, so it does not pin a real certificate; on a production name it would be the SHA-256 of the server's SPKI. Because isitdns.net is fully signed and anchored with a DS at .net, the record validates end to end, which is what makes a TLSA trustworthy.
delv (part of the BIND utilities) validates the DNSSEC chain automatically; the output will show ; fully validated when the chain is intact:
delv @1.1.1.1 _443._tcp.tlsa.example.isitdns.net TLSA; fully validated
_443._tcp.tlsa.example.isitdns.net. 300 IN TLSA 3 1 1 290B4C3FDBE4C7601988C018FA65B7DBD436FA53834A2D7A5FD29551 99CFE140
_443._tcp.tlsa.example.isitdns.net. 300 IN RRSIG TLSA 13 6 300 ...delv prints the validated RRSIG alongside the answer; its inception and expiration timestamps change every time Cloudflare re-signs the zone, so your copy will differ. The 300 is the zone TTL; a cached answer shows the remaining time, counting down.
On macOS the bundled
delv(BIND 9.10.6) has no crypto support and prints; no crypto supportinstead of validating. Install a current BIND (brew install bind) for a validatingdelv. ThedigandcurlDoH examples on this page work everywhere.
DoH query (HTTPS)#
DoH is DNS-over-HTTPS. The JSON format (application/dns-json) is a Cloudflare/Google extension, not defined by RFC 8484. The AD: true flag in the response confirms DNSSEC validated:
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=_443._tcp.tlsa.example.isitdns.net&type=TLSA' \
| jq .{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{
"name": "_443._tcp.tlsa.example.isitdns.net",
"type": 52
}
],
"Answer": [
{
"name": "_443._tcp.tlsa.example.isitdns.net",
"type": 52,
"TTL": 300,
"data": "\\# 35 03 01 01 29 0b 4c 3f db e4 c7 60 19 88 c0 18 fa 65 b7 db d4 36 fa 53 83 4a 2d 7a 5f d2 95 51 99 cf e1 40"
}
]
}AD: true confirms the DNSSEC chain validated. Not every DoH resolver decodes TLSA fields by name: Cloudflare returns the data field as RFC 3597 unknown-type hex (\# 35 = 35 bytes of RDATA, then 03 01 01 and the 32-byte hash, one byte per group), while Google's https://dns.google/resolve returns the same record already decoded as 3 1 1 290b4c.... The TTL counts down from 300 as the resolver's cache ages.
nslookup cannot show this here. Use dig or curl DoH to inspect the AD bit.
Generating TLSA values#
If you have the cert in PEM form, the most common case is usage=3, selector=1 (SPKI), matching=1 (SHA-256):
openssl x509 -in cert.pem -pubkey -noout \
| openssl pkey -pubin -outform DER \
| sha256sum290b4c3fdbe4c7601988c018fa65b7dbd436fa53834a2d7a5fd2955199cfe140 -That hex string is the TLSA RDATA for 3 1 1. Paste it without spaces into your zone file or DNS provider's UI.
hash-slinger (a small dedicated tool) automates this pipeline and emits zone-file form directly.
Gotchas#
- Cert rotation is a synchronization problem. Renew the cert; publish the new TLSA before serving the new cert. Otherwise validating receivers see a TLSA that does not match the cert and refuse the connection. The "two-overlap" rule: publish both old and new TLSA records during the transition, then drop the old one.
- DNSSEC is non-negotiable. A TLSA in an unsigned zone is ignored by every well-behaved validator. See dnssec-troubleshooting if your chain looks broken.
- Usage 3 (DANE-EE) is the common, pragmatic choice for endpoint pinning. Usages 0 and 1 require normal public-CA (PKIX) validation to also pass; usage 2 (DANE-TA) pins a trust anchor, such as a private CA, that the presented chain must validate up to; usage 3 says "trust this cert if it matches the hash, full stop." For SMTP, RFC 7672 section 3.1.3 says usages 0 and 1 SHOULD NOT be used at all.
- Wildcards.
_25._tcp.*is not a wildcard: DNS only allows*as the leftmost label (RFC 4592), so there is no way to blanket one port across many hosts. TLSA is per host and port; list each explicitly. - HTTPS browsers ignore TLSA. This bears repeating because it surprises people every year. Do not expect TLSA to authenticate
https://example.comto Chrome. - CAA and TLSA are complementary, not redundant. CAA tells CAs who is allowed to issue; TLSA tells the validating client (for mail, the sending MTA) which cert to accept from your server. Both protect mail; neither replaces the other.
See also#
- DNSSEC: TLSA is meaningless without it
- DNSSEC troubleshooting: fix a broken chain
- SSHFP: the SSH-side equivalent
- CAA: restrict which CAs may issue certs for your domain
- MX/SPF/DKIM/DMARC: the rest of mail-side hardening
- SRV: the same underscore-prefix naming convention
- DoT/DoH: DNS transport encryption (separate from DNSSEC)
- Query types: where TLSA (type 52) fits in the broader picture