Most ransomware post-mortems start at the wrong moment. They start with the ransom note, because that's the moment everyone noticed. But the note is the end of the story. By the time it appears, the attacker has usually had two or three weeks of quiet, unsupervised access to your filesystem — enough time to map your data, find your backups, and start degrading them before ever triggering an alarm.
That gap between "attacker gets in" and "attacker detonates" is where nearly every ransomware defense either works or quietly fails. And it's a gap that's much easier to close at the filesystem layer than at the backup-schedule layer.
Two ideas are worth separating out, because they solve different halves of the problem:
Neither is a silver bullet on its own. Together, they change what "attack surface" even means for the majority of a filesystem's data.
Backup strategy is usually framed as a scheduling problem: run the job more often, shrink your Recovery Point Objective, done. That's true as far as it goes, but it treats the attack as an instant event — as if encryption just happens and your only job is picking a recent-enough snapshot to roll back to.
In practice, ransomware is patient. It spends time in reconnaissance and lateral movement before it ever starts encrypting, and a meaningful fraction of that time is spent specifically looking for — and quietly disabling or corrupting — your backup infrastructure. A nightly job with no visibility into how files are being accessed has no way to notice this happening. It just keeps faithfully backing up a system that's already compromised.
This is where an access record earns its keep. On Linux, fanotify is a kernel API originally built for things like antivirus scanning and data-loss-prevention tooling — it lets a userspace process subscribe to filesystem events (opens, reads, writes, moves) across a mount, including the option to intercept and permit or deny the operation before it completes.
Unlike periodic log scraping or inotify-style watching of a narrow directory tree, fanotify can observe an entire mount with the kernel itself as the source of truth, which matters a lot when the thing you're worried about is a process specifically trying to evade file-integrity monitoring.
A minimal listener looks roughly like this:
int fan_fd = fanotify_init(FAN_CLASS_CONTENT, O_RDONLY);
fanotify_mark(fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
FAN_OPEN_PERM | FAN_ACCESS_PERM | FAN_CLOSE_WRITE,
AT_FDCWD, "/data");
struct fanotify_event_metadata buf[200];
ssize_t len = read(fan_fd, buf, sizeof(buf));
// inspect each event: pid, path (via /proc/self/fd/<fd>), event mask
What you get from this isn't just a log — it's a live stream of ground truth about file access, with enough context (which process, which file, read vs. write, how fast) to build behavioral detection on top of it. A single process suddenly issuing thousands of open-and-rewrite operations across directories it's never touched before is a very different signal than "the nightly job ran." You can act on the former in seconds. You typically don't even see the latter until someone reports a locked screen.
The practical upshot: an access record turns detection from a forensic exercise ("when did this start?") into a real-time one ("this is starting, right now, and here's exactly which files and processes are involved"). That's the difference between recovering with a known, tight blast radius versus recovering with a shrug and a two-week-old backup.
The second half of the problem is more structural. Mass-encryption ransomware works by enumerating a filesystem — walking directories, opening files, encrypting them in place, deleting or overwriting the originals. This works because most of an organization's data, including data nobody has touched in months, sits as ordinary files on ordinary mounts, fully enumerable and fully writable by anything running with the right privileges.
A NAS share doesn't change this. Neither does a cloud backup bucket, if it's reachable with credentials that live on the compromised host. All of these are still, from the malware's point of view, just more paths to walk. "Networked" and "offsite" are not the same property as "not enumerable."
The more interesting move is to stop keeping cold data as individually addressable files at all. Instead of a directory full of files that any process can list and open, cold data can be packed into a small number of large, append-only archive containers — with the live filesystem holding only a lightweight stub or catalog entry pointing at where the real bytes live. There's no longer a contract_2026.docx sitting on disk for a for file in * loop to find and encrypt; there's an entry in an index, and a compressed frame inside an archive that isn't mounted as a writable filesystem path at all.
This is where zstd's framing format turns out to be a genuinely good fit for archival, rather than just a compression detail. Zstandard data is organized into independent frames, each with its own header and checksum, and the format is explicitly designed to support concatenation — you can append a new frame to an existing archive without touching or re-encoding anything that came before it. That gives you a few properties that matter specifically for ransomware resistance:
os.walk() can iterate and act on file-by-file. Ransomware built around "find files, open, encrypt, replace" simply has nothing to enumerate.Combine that with the access record from the first section, and you get defense in depth that actually compounds: the archive structurally removes most files from being individually attackable, and the fanotify layer watching reads against that archive (or against the stub layer that fronts it) can catch the narrow slice of anomalous behavior that's left — a sudden burst of restore requests, for instance, which is exactly what a "low and slow" ransomware run trying to pull and re-encrypt archived data would produce.
The net effect isn't just "better backups." It's a different shape of exposure. Your Recovery Point Objective stops being tied to a schedule and starts being tied to whenever a file was last written — because the event stream, not a nightly job, is what's deciding when data moves into the archive. And your blast radius stops being "everything on this filesystem" and shrinks to "whatever was still sitting in the live, hot tier when the attack started," which for most organizations is a small fraction of total data.
None of this replaces good backup hygiene, offline copies, or incident response planning. But it does move the fight to a stage where you have more leverage: instead of racing to restore from whatever snapshot survived, you're dealing with an attacker who never had a normal filesystem to attack in the first place.
If you want to see this pattern implemented end-to-end — fanotify-based interception, an open SQLite catalog, and append-only archiving with tape/WORM support — HuskHoard is an open-source (AGPL v3) active archive system built around exactly this model. Worth a look if you're building something similar.