We audited our own website the way we audit a client's
Four findings on cyberaar.io, including TLS 1.0 still accepted at the edge and a playbook that reported success while doing nothing. Commands included.
A security vendor’s website is the first thing a prospect tests. Not its offering: its HTTP headers. It is free, it is anonymous, and it answers a question nobody asks out loud in a meeting: do these people apply at home what they are about to invoice us for?
So we ran cyberaar.io through the same set of checks we run for a client. Here is what we found, including what was wrong.
Finding 1: no CAA record, and a trap in the fix
A CAA record (Certification Authority Authorization) declares, in DNS, which certificate authorities are allowed to issue a certificate for your domain. In its absence the default answer is: all of them. The domain had no CAA at all. That is not an exploitable vulnerability in itself, it is a missing constraint: nothing in DNS stopped an arbitrary CA from issuing a valid certificate for cyberaar.io.
The fix looks obvious. We use Let’s Encrypt, so authorise Let’s Encrypt and close the rest:
cyberaar.io. IN CAA 0 issue "letsencrypt.org"
That is precisely what you must not do behind Cloudflare, and it is the finding that justifies this article.
The certificate a visitor sees is not ours: it is the one Cloudflare presents at
the edge, through Universal SSL. And Cloudflare does not always obtain that
certificate from the same authority. The service runs across several CAs, and the
authority in use can change from one renewal to the next. A CAA restricted to
letsencrypt.org therefore works perfectly on the day you set it, then breaks
edge renewal at the moment Cloudflare rotates to a different authority.
The outage would arrive weeks after the change that caused it, and would surface as a certificate error for visitors with no recent modification to blame. That is the worst possible failure profile.
The set actually authorised therefore covers the authorities the service draws
on, letsencrypt.org, pki.goog, ssl.com and sectigo.com, plus an iodef
address so we are notified of a refused issuance attempt.
Two points that prevent a false alarm at the next review:
- Once any CAA exists on the zone, Cloudflare automatically completes the set with the authorities it needs. The zone ends up showing about a dozen records where five were declared. That is expected behaviour, not configuration drift.
- While DNS stays at Cloudflare, what the declared list actually guarantees comes
down to the
iodefaddress and to the set being correct on the day DNS moves elsewhere. It is worth knowing what you are buying with this control: it is not a lock, it is a declarative constraint and an alert.
Finding 2: security.txt had two sources of truth
The /.well-known/security.txt file (RFC 9116) states how to report a
vulnerability. It carries a mandatory Expires field: past that date the file is
no longer compliant, and a researcher is entitled to conclude the channel is no
longer maintained.
Ours existed twice: a static file in the site sources, and a template generated at deploy time with a computed expiry date. Both were deployed. The second was overwritten by the first, because the static file sync ran after the template was generated and deleted anything that did not belong to it.
The concrete consequences: the Policy line never reached production, and the
expiry date was frozen at a hardcoded value instead of being recomputed. The file
was therefore going to become silently non-compliant, on a date known in advance,
with nothing to flag it.
The static copy was deleted and template generation now runs after the sync. The resulting rule is a general one: a file that has to expire cannot be a static asset. If it carries a date, it must be produced by whatever deploys it.
Finding 3: the playbook reported success while doing nothing
This one is not a defect in the site, it is a defect in the method, and it is probably the most useful of the three.
The tasks that set the CAA records depend on a value produced by an earlier task, which resolves the DNS zone. Those two tasks did not carry the same execution tags. Running the deployment restricted to the DNS tasks therefore skipped the zone lookup, and every CAA task that depended on it did nothing.
The deployment finished by reporting success. The final reporting task ran normally and printed the expected result. Nothing in the tool’s output distinguished that run from a successful one.
The gap was caught only by querying an external DNS resolver, which returned no CAA. Hence the rule we have applied since: a DNS or TLS change is verified against an external resolver, never against the tool that made it. A deployment tool reports what it attempted, not what the world observes.
Finding 4: the one writing this article produced
The final section of this article offers four verification commands. We ran them against our own domain to make sure they were correct. The last one answered something other than what our notes claimed.
Our internal notes carried the line “TLS 1.3 only, TLS 1.0 and 1.1 refused”, filed under checks that were verified and closed. The reality, measured from outside:
Protocol : TLSv1
Cipher : ECDHE-ECDSA-AES128-SHA
A complete TLS 1.0 negotiation. Not a configuration warning: a completed handshake, on the domain of a security company.
Why the note was wrong is the interesting part. The original test had in all likelihood been written like this:
openssl s_client -connect cyberaar.io:443 -tls1_1
On OpenSSL 3.x that command fails with no protocols available whatever the
server is configured to do: obsolete versions are disabled on the client side.
The failure looks exactly like a rejection by the server. Someone ran the
command, saw a failure, concluded TLS 1.1 was refused, and closed the item. That
is finding 3 in a different costume: we had verified the tool, not the world.
The technical cause is worth knowing beyond our case. Behind a CDN, the minimum TLS version is an edge setting. Cloudflare terminates the visitor’s connection, not our reverse proxy. TLS hardening applied at the origin therefore has no effect on what a visitor can negotiate, and the default value of that edge setting is TLS 1.0. Many teams harden the origin carefully and leave the edge at its factory value: the surface actually exposed is the second one.
Corrected on 20 August 2026, minimum version raised to TLS 1.2 in SSL/TLS > Edge Certificates > Minimum TLS Version.
Two lessons, one of which costs us something to write:
- A “verified” note has an expiry date, and ours was wrong from the start on top of that. A register of checks is not evidence, it is a memory.
- Check the edge, not the origin. The visitor never talks to your server.
What was already in order
An honest audit publishes what passes too, otherwise it measures nothing. Verified from the outside:
- No cookies. The site sets none, which is why there is no consent banner. That is not an oversight, it is the consequence of running no third-party analytics.
- HTTP/3 available. TLS 1.3 negotiated by default, TLS 1.2 the floor since finding 4.
- No exposure of
.git,.envor source maps. No directory listing. TRACE,TRACK,PUTandDELETEall answer 405.- No permissive CORS headers on the API.
- The content security policy was tightened at the same time, with
object-src 'none'andupgrade-insecure-requests.
HSTS preload: a commitment, not a checkbox
The domain has been submitted to the HSTS preload list. That means browsers will refuse any unencrypted connection to cyberaar.io and its subdomains without even attempting the request.
It is a decision to take deliberately, because it is hard to reverse: removal is measured in months, not minutes. In practice, every browser-facing subdomain must serve HTTPS, permanently. In our case the constraint costs nothing, since everything already sits behind a reverse proxy with automatic certificates. It deserves to be evaluated beforehand, not afterwards. Note that HSTS preload concerns browsers only: agents and API calls are unaffected.
Check your own
None of the above requires special tooling. These four commands answer the essentials, on your domain as well as on ours:
# Which CAs may issue for your domain? (empty = all of them)
dig CAA example.com +short
# Is HSTS advertised, and for how long?
curl -sI https://example.com | grep -i strict-transport-security
# Does the reporting channel exist, and has it expired?
curl -s https://example.com/.well-known/security.txt
# Which TLS version is actually negotiated?
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| grep -E "Protocol|Cipher"
To test whether an obsolete version is still accepted, you have to lower the client’s security level, otherwise you are measuring your own workstation rather than the server, as in finding 4:
openssl s_client -connect example.com:443 -servername example.com \
-tls1 -cipher 'DEFAULT@SECLEVEL=0' </dev/null 2>&1 | grep -E "New,|Protocol"
If the answer contains TLSv1, the server still accepts TLS 1.0. Behind a CDN,
the setting to correct is the edge one, not your server’s.
If the first command returns nothing, start there, and read finding 1 before you set the obvious line.
We publish this because our commercial argument is that our clients can inspect
what we operate. That starts with our own domains. A mistake, an objection, a
technical disagreement: [email protected].