Reading a CT log: your domain's certificate history
Certificate Transparency was designed to detect rogue certs. Every cert a public CA issues lands in append-only public logs within 24h. The honest goal: someone notices when a CA mints a fraudulent cert for your domain and you can respond.
The unintended consequence: every cert your team has ever requested is also public,
forever. Old internal hostnames, mid-migration testing certs, the email server you
forgot to retire — all on file at crt.sh. Attackers query first, then
poke.
Reading your own logs
The simplest queries:
# Web UI
https://crt.sh/?q=%25.example.com # all subdomains
https://crt.sh/?q=example.com # apex + subs
# JSON API
$ curl -s 'https://crt.sh/?q=example.com&output=json' \
| jq -r '.[].name_value' | sort -u | head
admin.example.com
api.example.com
example.com
mail.example.com
old-staging.example.com <-- still exists?
www.example.com
Run that against your apex. The list is everything attackers see when they're enumerating your attack surface. Each entry is a hostname for which someone, somewhere, has at some point requested a TLS cert.
What surprises people
- Internal hostnames you thought were private. If your dev team got an LE cert for
jenkins-internal.example.comin 2020, it's there forever, even if the host has been gone for 4 years. - Pre-migration apex certs pointing at the old origin IP. Even if your DNS now points at Cloudflare, the cert from 2022 still records the original cert + chain.
- Customer-named subdomains. SaaS that issues per-tenant subdomains (
customer-acme.app.com) leaks every customer's name through CT. - Wildcard certs leaking nothing — actually, ironically, wildcard certs DON'T enumerate subdomains.
*.example.comis one CT entry that covers everything. Worth considering.
Detecting rogue issuance
The original CT promise. If you see a cert in your logs that you don't recognise, someone else convinced a CA to mint a cert for your domain. Investigate immediately — likely scenarios:
- Compromised registrar / DNS provider — attacker passed DNS-01 challenge briefly.
- Compromised internal CA automation — an old service account still has ACME credentials.
- Forgotten Cloudflare / Vercel / Netlify config that's silently issuing certs in your name.
- Actual CA compromise (rare; usually a major news event).
Pair with CAA (see our CAA article) so only your authorised CAs even attempt issuance.
Set up alerting
You can write a daily cron that fetches crt.sh JSON for your domain, diffs
against yesterday's snapshot, alerts on new entries. ~30 lines of bash.
Or use UnveilScan's CT monitoring feature: register a domain in
/ct-watches, we poll crt.sh hourly, fire ct_new_cert events
via your alert channels (email / webhook / Slack) when a new fingerprint appears. Free
tier covers 1 watched domain.
The privacy implications
CT is irreversible. Once a hostname is in a CT log, it's there forever. Practical implications:
- Don't put secrets in hostnames.
customer-12345-internal-debug.example.comreveals you have customer 12345 forever. - Use wildcard certs for non-public subdomains. One
*.internal.example.comentry beats 200 individual entries that enumerate your topology. - Be careful with staging / preview environments. If they're behind auth but cert-issued, they leak naming.
Tools beyond crt.sh
- Certificate Search (
crt.sh) — the canonical free index. - Censys — paid, indexes CT plus internet-wide scans.
- Sublist3r — Python tool that combines crt.sh + DNS bruteforce + search engines.
- SecurityTrails — paid, has stronger historical DNS data.
- UnveilScan — uses crt.sh for subdomain enumeration + takeover detection in the Extended profile.
Audit your CT log footprint
Extended scan enumerates subdomains via CT and probes for takeover-vulnerable ones.
See pricing