Implementing a CAA Record
It has been a while since tools like Qualys’ SSLLabs and testssl.sh are reporting on the usage of CAA records. So anyone caring about the quality and security of their SSL connection will probably have noticed its existence by now. But what is it for and how do you configure it?
In short: using a (DNS) CAA record you can specify which Certificate Authorities are allowed to issue certificates for your domain names. Because a lot of good resources about this topic already exist I won’t go into detail about that; if you want to know more, you could try wikipedia for example.
Finding out how to correctly configure a CAA record is not so obvious though, because there is not that much information online yet. I’ve done an implementation for a client last summer and ran into some issues and questions that I will cover here in this post.
The value of the CAA record
Most information points to sslmate as a good tool to generate the correct value. While I won’t deny that it probably provides accurate results in most cases, it is good to keep in mind that this resource is incomplete and can therefore also be inaccurate. My first SSL request after placing the CAA record was denied because the value this tool produced was incorrect; the reason for that is explained in the paragraph below (Hierarchy of Issuers)
My search for other sources were all dead ends:
- I first tried the certificate itself. After turning it inside out and upside down with openssl I had to conclude that the information cannot in any way reliably be deducted from the information in an existing certificate.
- I then tried the Certificate Transparency Logs. It was a long shot, but I tried it nevertheless – with no result.
Conclusion: as far as I know there is no reliable way of determining the value of this record other than ‘contacting your Certificate Authority’. They might have published it on their website (in various formats) and if not, I suggest you contact them.
Hierarchy of Issuers
As mentioned above, my first certificate request (after publishing the caa record) was denied. The sslmate-tool had indicated I should use X1 in the value of my CAA record. This seemed correct as my client was actually using a subsidiary of X for most of their certificates. The reason my request was denied is that this subsidiary (X.C2) had (at that time) chosen to deny requests for a domain if that domain had specified its parent CA (X) in the CAA record.
I have visualized this in the following picture:
The sslmate-tool had blindly assumed that all subsidiaries of Certificate Authority X would honour certificate requests with X in the value of the CAA record. But subsidiary X.C only accepted requests with a CAA specifying them explicitly, so my request was denied.
After I found the correct value to authorize them directly in my CAA record, it worked:
With the correct CAA value in place, I could now successfully request certificates again via X.C
What is interesting about this, is that there is a hierarchy mechanism in place, that you don’t read about in the docs. When a CA accepts CAA values that reference them directly and CAA values that reference the parent CA, then you can decide, as a user, to:
- Only trust that specific CA (so, X.C in the example)
- Trust the parent CA and all their siblings (X, X.A & X.B as well).
If you work in/for a large company, you will probably want to have an overview of the current Issuers in use before proceeding with the placement of the CAA record. Depending on how big the company is and/or how much online activity it has, this can be a challenge.
Basically you have two sources you can use:
- Certificate Transparency Logs
- Checking all the domains/certificates manually
While the Certificate Transparency Logs form a really good resource, chances are big it’s incomplete for you. But I’m also not a big fan of doing manual work, so I decided to write a little script that you may find useful as well. We’ve made it public on our github account here:
This script will output the issuer for any given domain or any errors it may encounter while resolving the domain name or connecting to it. The readme in the repository also covers how you can execute this in batch for a large number of domains and filter the output to not show any errors.
CAA and cnames3
While there are some good resources on CAA records, this aspect isn’t discussed much and when it is, it is as often wrong as it is correct.The problem is this: if the domain
example.com has a CAA record then this value is valid for all subdomains, unless a subdomain specifies otherwise. But what happens when the subdomain
sub.example.com is a CNAME record with the value
anotherexample.com has its own CAA record?
Unfortunately, a lot of contradictory information can be found. There are resources that claim the parent hierarchy of the alias (the value the CNAME points to) is checked if the alias itself doesn’t have a CAA record and there are also resources that claim the opposite (parent records of the alias are not checked).Just to be clear: the parent hierarchy of the alias is not checked (or at least should not be checked)
Thus, when a search at node X returns a CNAME record, the CA will follow the CNAME record chain to its target. If the target label contains a CAA record, it is returned. * Otherwise, the CA continues the search at the parent of node X.
And to remove the last bit of doubt, it even makes the following note:
Note that the search does not include the parent of a target of a CNAME record
However it seems the damage has already been done, because many websites still explain the model according to the original – incorrect – logic. I could also find at least one example of a CA that implemented the original logic, as this bug report shows. So, my advice here is to pay careful attention as your CA might still use the old logic from the original RFC.
Public Key Pinning
CAA records can form a nice alternative for Public Key Pinning. In short: Public Key Pinning is a technique where you instruct a client to not trust any certificates with a public key that doesn’t match with the one that was stored (pinned) previously. While this makes Man In The Middle attacks very difficult, it also poses a huge risk : if something happens that makes you need to change the certificate on the server, then there’s no way of informing your clients and you simply have to wait until the TTL expires. This can have huge consequences. In fact, this technique has never been widely adopted and Google (the initiator) has also abandoned it late 2017 and marked it as deprecated.
CAA forms a nice alternative. Public Key Pinning prevents malicious actors from using valid (but illegally obtained) certificates to intercept traffic. Using CAA you can ‘more or less’ accomplish the same thing, because you’re narrowing the scope of Certificate Authorities that can issue a certificate for your domain. It does not provide the same level of security, but it does go a long way. Additionally you could monitor the Certificate Transparency Logs and optionally deploy an Expect-CT header.
Some final thoughts
An aspect that is also relevant, but often forgotten: CNAME records cannot be combined with other records, except for SIG, NXT and KEY records (see RFC2181). With regards to these specifications I find that the current CAA specs left a loophole. By relying on DNS records, it is crucial that you are in control of the DNS, which is one of the reasons why DNSSEC is strongly recommended. But because you cannot set a CAA record on a domain that has a CNAME value, you leave a small loophole because the external DNS will have the power to specify a CAA record for that domain and this will have precedence over CAA records specified on the parent record. It would have been nice if the specs allowed for a way to switch off this behaviour or overrule it by other means.
1 X was not the actual value, but to keep it generic I’m using this name in all my examples.
2X.C is also not the actual value, but used as an example to show the relationship with X
3A cname is a DNS record in which you map one domain name to another, thereby creating an alias.