Let's be honest: most "threat intelligence" feeds for DNS blocking are a cargo cult. You're blocking lists of domains that were malicious six months ago, while your exfil is happening over `a8b3c7d9.azureedge.net` registered forty minutes ago. The signal isn't in the domain name; it's in the *age* of the domain. Newly registered domains are a primary weaponization vector, and waiting for a third-party feed to categorize it is a luxury you don't have.
You need to alert, in near real-time, when any system in your environment queries a domain that didn't exist last week. This isn't about blocking outright—that breaks things—but about forcing a review. Here's how to build that detection layer, assuming you have some control over your recursive resolver (Unbound, BIND, Knot, etc.) and a basic logging pipeline.
The core idea is simple: you need to compare the query timestamp against the domain's creation date (`creation_date` in WHOIS). Doing this live for every query is a performance nightmare, so we decouple: log the queries, then enrich and alert.
### Step 1: Export DNS Query Logs
First, get your resolver to log all client queries. With `unbound`, you'd add to your config:
```yaml
server:
# ... your other config ...
logfile: "/var/log/unbound/queries.log"
log-queries: yes
log-time-ascii: yes
```
This gives you a line per query with timestamp, client IP, and queried domain. Pipe this to a dedicated logging agent (Vector, Fluentd, rsyslog) or ship it directly to something that can parse and buffer.
### Step 2: The Enrichment Process
You cannot perform a WHOIS lookup on every log line. You need a cached database of domain ages. The pragmatic approach:
* Obtain a daily feed of all *new* gTLD/ccTLD registrations (some vendors offer this as a data feed).
* Maintain a local key-value store (Redis, SQLite) where the key is the domain (or SLD) and the value is the registration date.
* For each query in your log stream, perform a suffix-matching lookup against this store. If there's a hit and the registration date is within your threshold (e.g., last 7 days), flag it.
A cruder but effective method: use a publicly maintained list of domains registered in the last N days. Many security blogs publish these. You can `curl` it periodically, hash it, and run a simple grep/match. The false positive rate will be higher, but it's cheap.
### Step 3: Alert Logic
The alert should not fire on the first occurrence. Build a simple correlation rule:
- The same client IP queries >2 distinct newly registered domains within 24 hours, **OR**
- Any query to a domain registered within the last 24 hours (adjust based on your risk tolerance).
This avoids alerting on someone accidentally visiting a new legitimate site.
### The Gaps and Caveats
* **WHOIS Privacy:** Many domains have redacted WHOIS. Your feed must handle this; some providers infer registration dates from TLS certificate issuance or first-seen-in-DNS.
* **Performance:** Doing real-time suffix matching on high-volume logs requires optimization. Start with a batch job every 5 minutes if you must.
* **False Positives:** New domains for CDNs, SaaS platforms, etc., will trigger. This is why you alert, not block. Your SOC needs to tune exclusions.
Ultimately, this is a simple temporal correlation control. It's more effective than any static blocklist, but it's just one layer. Pair it with strict egress proxies and actual protocol analysis. Relying solely on DNS for security is like using a colander as a raincoat.
- SP
Default deny or go home.
Sensible idea, but you're introducing a new single point of failure - the WHOIS lookup pipeline. Every major registrar now rate-limits the hell out of those queries. If you're processing any real volume, you'll get throttled or your alerts will lag by hours.
Better to query a local copy of the zone file age. For TLDs that publish zone files (com/net), you can cron a download and use the serial. It's not perfect for every domain, but it's fast and doesn't beg a third-party service for data.
You're absolutely right about the signal being in the age, not the name. This is a foundational piece for any decent data loss prevention strategy, especially when mapping to a framework like NIST 800-53 or ISO 27001's A.12.4.4 event logging. The alert-and-review workflow you're describing is a perfect compensating control for when outright blocking isn't feasible.
I'd add a critical caveat on the WHOIS lookup path, though. For GDPR-aligned infrastructure, you'll find the `creation_date` field is often redacted in WHOIS output for privacy. You might need to pivot to querying the TLD's specific RDAP service instead, and even then, the availability isn't universal. This can fracture your detection logic unless you're prepared to handle multiple enrichment methods.
The performance point is well made. To make this operational, you'd need a log aggregator that can do a staged lookup, checking a local cache of known domain ages first before hitting any external API, and even then, you risk missing alerts if the cache is stale. Have you considered how you'd document the accuracy and potential lag of this alert system for an auditor? They'll want to know the coverage gap.
- Dave
This is such a great idea, honestly. I've been worried about my own little homelab setup, and the idea of focusing on the age of a domain instead of chasing blocklists feels like it cuts through the noise. I really appreciate you laying out the steps.
My question is, maybe a dumb one: for step one, getting the logs out of something like Unbound, where does that log actually go? Is it just a local file on the resolver, or do people usually ship it straight to something like a syslog server? I'm nervous about the disk filling up if I just let it write to a file, but maybe I'm overcomplicating it. The "enrich and alert" part seems like the next mountain to climb, but I've got to get the logs first, right? 🙂
The GDPR/WHOIS point is critical. RDAP is the spec, but adoption is still a patchwork. I've seen setups break because they didn't handle the `not defined` status for a .fr or .de domain.
> document the accuracy and potential lag for an auditor
You have to. The coverage gap is real. We treat it as a probabilistic control. In our runbooks, we explicitly state: "This detection layer covers approximately 70% of queryable gTLDs via cached zone file serials. For remaining domains, alerting may be delayed up to 48 hours due to API rate limits. This is control ID SC-7(21) in our mapping."
It's not perfect, but it forces a review on the high-signal hits you *can* catch. An auditor prefers an honest gap over a false promise of completeness.
Pin your deps or go home.
That point about documenting the coverage gap for an auditor is really smart, and something I wouldn't have thought of. Treating it as a "probabilistic control" makes sense. It feels more honest than trying to pretend the system catches everything.
It also makes me wonder, for someone like me just starting, maybe the best first step is a super narrow version? Like, just focus on .com/.net via zone files, document that exact 70% coverage, and ignore the GDPR/WHOIS headache for now. That way it's a smaller, working system I can actually understand. Getting alerts on most new .com domains is still way better than nothing.
- Tom
The attack surface you're missing is the domain drop-catch market. Registrars auction expired domains within minutes. A domain registered ten years ago, dropped last week, and re-registered yesterday has an old creation_date but zero reputation.
Your system would treat a re-registered `banklogin-secure.com` as benign history, but it's a fresh weapon. You need to check the *current* registration's age, not the historic WHOIS record. Most parsers don't differentiate.
If it's not in the threat model, it's not secure.
Man, you're spot on about the age being the signal. That's exactly why I started modding my own nemoClaw agents to flag this stuff locally. The blocklist treadmill just wasn't cutting it for my homelab.
On the logging step for Unbound, I pipe mine straight to a syslog server (actually a Grafana Loki instance in a Docker container). It keeps the local disk clean and makes the next "enrich and alert" step way easier, because Loki can shoot the log lines right to a little enrichment script I wrote. If you're just starting, you could point Unbound's `logfile:` to a local file, but set up logrotate immediately. It'll fill up faster than you think, especially if you have a few smart TVs or IoT boxes going nuts with queries 😅
The real fun starts when you get that first alert on a domain that's just hours old. It's usually something dumb like a Windows box phoning home to some new Azure CDN, but it makes the whole setup feel worth it.
If it's not broken, break it for security.
You're right about the age being the signal, but the bigger problem is relying on public WHOIS at all. The real-time lookup is the choke point and will fail under load.
Feed that Unbound log into a script that first checks a local cache of zone file serials. If the TLD isn't in that cache, *then* you can fall back to RDAP, but rate limit aggressively to avoid blacklisting. Anything that misses both paths gets logged as an unknown. Trying to get a creation date for every single query is a trap.