Leadership
We should block the RMM! We should block that IP! We should block that port! The trouble is; there’s 65535 ports per protocol (TCP/UDP), there’s billions of IPs (dynamic and rotating infra are a thing), and well that’s a lot of work, neve ending, in fact!!
This topic has come up a number of times recently so I thought I’d put it into a blog, so I had Claude generate this based on my quick fire views:
Allow list vs Deny List, or why not both?
You Cannot
Block Infinity
Every blocklist is a bet that you can enumerate the set of all bad things. You can’t. It’s unbounded, and your adversary gets to keep adding to it. So stop subtracting — start with deny, and allow only what you actually need.
◤ deny known-bad
◢ allow known-good
left: a list that never finishes. right: a list you can actually hold in your head.
There is a comfortable lie baked into most enterprise security programmes, and it goes like this: if we buy enough feeds, tune enough signatures, and subscribe to enough intel, we can block the bad stuff. It feels like progress. Every new indicator added to the firewall, every fresh hash in the EDR, every domain sinkholed — it all looks like ground being won.
It isn’t. It’s a treadmill with a very expensive subscription. And the reason it can never work is not operational, it’s arithmetic.
01 / THE ASYMMETRYThe complement of a finite set is infinite
Blocking known-bad is an attempt to enumerate a set: all the IPs, domains, hashes, user-agents, JA4 fingerprints, and byte-sequences that represent an attack. Call that set B. Your entire denylist strategy rests on the assumption that you can, in practice, describe B.
You can’t, because B is not fixed and it is not yours. An adversary generates new members of it on demand and at near-zero cost. A hash changes if a single byte flips. A C2 domain costs a few pence and lives for hours. IP space is rented by the hour. A packer produces a fresh, never-before-seen binary every time it runs. Domain generation algorithms mint thousands of resolvable names a day. The set of bad is adversarially expanding — it grows precisely as fast as it needs to in order to stay ahead of whatever you’ve written down.
Now consider the other set. G — the things your organisation legitimately needs to do. Finance touches your ERP, your banking portal, Microsoft 365, and a payroll SaaS. Engineering touches your git host, a package registry, and a handful of cloud consoles. Your domain controllers talk to each other and to precisely nothing on the open internet. A CNC controller on the shop floor speaks to one historian and one engineering workstation, forever.
B is unbounded and belongs to your enemy. G is finite and belongs to you. Any strategy built on enumerating B is a strategy built on losing.
This is the whole argument in one line: the complement of a finite, known-good set is an infinite, unknowable-bad set. You will never finish blocking bad. You can finish writing down good — and more importantly, you can maintain it, because it changes at the speed of your business, not at the speed of the global threat landscape.
02 / THE TREADMILLWhy the denylist feels productive but isn’t
David Bianco’s Pyramid of Pain made this concrete years ago, and it still holds. At the bottom of the pyramid sit hash values, IPs, and domains — trivial for an attacker to change, which means blocking them inflicts almost no cost. At the top sit tactics, techniques and procedures — expensive to change, because they reflect how the adversary actually operates. Denylists overwhelmingly operate at the bottom of the pyramid. You are spending real money and real analyst hours to impose trivial, momentary inconvenience on your opponent.
Indicators of compromise are also the most perishable form of intelligence there is. Today’s confirmed-malicious IP is next week’s recycled cloud address serving something legitimate — that’s how you accrue false positives and how the feed slowly poisons your own operations. Meanwhile the genuinely dangerous thing — the targeted intrusion, the fresh zero-day, the bespoke loader written for you specifically — is by definition not on anyone’s list yet. A denylist can only ever contain what someone, somewhere, has already been hit by and reported. It is structurally a record of the last war.
03 / FLIP THE DEFAULTDeny everything, allow known-good
The fix is not a product. It’s a change to the default. Instead of permit, then subtract the bad you happen to know about, you default-deny, then add back the good you can enumerate. This is the spine of Zero Trust (NIST SP 800-207), of least privilege, and of application allowlisting (NIST SP 800-167) — the same principle expressed at different layers of the stack.
- Egress. Most networks have wide-open outbound. That openness is the exfil path and is the C2 channel — beaconing to an arbitrary internet only works because the internet is reachable. Default-deny egress, allow named destinations per segment, and the majority of commodity C2 simply cannot phone home.
- Applications. WDAC, AppLocker, and Google Santa let you run only signed, approved binaries. This is the single highest-leverage control against commodity malware and a large fraction of living-off-the-land abuse — an unknown executable doesn’t get a signature check, it gets refused.
- DNS. Resolve what’s needed, sinkhole the rest. Newly registered and uncategorised domains have no business resolving on a server VLAN.
- Identity. Conditional Access is the modern control plane: this user, in this group, on a compliant device, from an expected location, with phishing-resistant auth, to this specific application — and deny by default for everything outside that envelope.
Notice what’s happening. In every case you’ve replaced “guess all the ways in” with “describe the narrow way through.” The first is infinite. The second is a spreadsheet.
04 / SCOPE IS THE POINTNot “allow good” — allow good, for whom
Here’s where most allowlisting quietly fails: people build one flat, global allowlist, over-provision it so nothing breaks, and end up with a permit-any in a trench coat. A flat allowlist is barely better than a denylist. The power is entirely in the scoping.
Finance’s legitimate footprint is not engineering’s. Engineering’s is not the receptionist’s. And none of them look anything like the domain controller, the print server, or the PLC. When you scope allow-rules to user roles and device groups, two things happen: each individual list gets small enough to actually reason about, and least privilege stops being a slogan and becomes the literal shape of your ruleset.
| Group | Legitimately needs | Everything else |
|---|---|---|
| finance-grp | ERP, banking portal, M365, payroll SaaS | denied |
| eng-grp | Git host, package registry, cloud consoles | denied |
| domain-controllers | Replication peers, NTP, patch source | denied |
| ot-cell-3 | One historian, one engineering workstation | denied |
Operational technology is where this becomes almost undeniable. A CNC machine or a PLC has one of the most predictable communication profiles in all of computing — a tiny, static set of peers and protocols that does not change from one year to the next. On a segment like that, denylisting is close to negligent and allowlisting is close to trivial: define the zone, define the conduit (IEC 62443 gives you the vocabulary), permit exactly that, deny the rest. For the manufacturing and aerospace-supply-chain crowd wrestling with TISAX, CMMC and NIS2, this isn’t extra work bolted on for compliance — scoped access control (NIST 800-171 3.1.x, the CMMC AC family) is the requirement, and default-deny is the cleanest way to satisfy it.
05 / THE HONEST OBJECTIONSWhy people don’t do it (and why that’s not a reason)
Allowlisting is harder to start. Pretending otherwise is how these projects die in month two, so let’s be straight about the costs.
You have to know your environment
You cannot allow what you cannot see, which is exactly why CIS Controls 1 and 2 — inventory of assets, then inventory of software — come first and everything else comes after. Most organisations that fail at allowlisting didn’t fail at allowlisting; they failed at inventory and hit the wall the moment they tried. The discovery work is the real project. The rules are the easy part.
There is genuine operational friction
New app means a change request. Shadow IT will resent it. Break-fix happens. But look closely at that maintenance burden: it is bounded by your rate of legitimate change, which is slow and knowable, not by the adversary’s rate of innovation, which is fast and infinite. You’ve traded an unwinnable race for a manageable backlog. That’s a good trade.
It is not either/or
06 / FAILURE MODESLoud and safe beats silent and fatal
This is the argument I’d leave you with, because it outlives any specific control. Compare how the two strategies fail.
A denylist fails open. When it’s wrong — when the bad thing wasn’t on the list, which is the normal case — the failure is silent. The traffic passes, the binary runs, the beacon goes out, and you find out weeks later during incident response, if you find out at all. The failure mode is invisible and catastrophic.
An allowlist fails closed. When it’s wrong, it blocks something legitimate. That failure is loud: a user rings the service desk, a ticket gets raised, someone adds a rule, and everyone moves on within the hour. Annoying? Yes. Survivable? Always.
You get to choose the shape of your failures. Denylisting chooses silent and fatal. Allowlisting chooses loud and safe. Choose the one you can live through.
07 / WHERE TO STARTYou cannot boil the ocean
Don’t try to allowlist everything on Monday. Go where the ratio of value to effort is best, and let each win fund the next:
- Default-deny egress on server and OT segments first. Small, predictable, high-value. Servers rarely need to reach the open internet, and OT never should.
- Application allowlisting in audit mode, then enforce. Run it observe-only, learn the legitimate baseline, then flip it. WDAC and Santa both support this pattern.
- A Conditional Access baseline. Compliant device plus phishing-resistant auth, scoped per group, as the identity default.
- DNS filtering with a default posture that blocks newly-registered and uncategorised domains, especially on non-user segments.
- Then tighten, progressively. Every scope you define shrinks the infinite set you were pretending to police down to a finite one you actually control.
You will never win by subtracting bad from infinity. You win by describing good — narrowly, per group, per device — and denying the rest by default. It’s more work at the start and far less work forever after, and it fails in the direction that lets you sleep.
As you can see, the task is not a flick of a switch! That’s why a blended approach often works. Also using PDNS, IPS/IDS, dynamic block lists etc. also help, but the goal should be to move to ALLOW from specific viewpoints!








