Blog · Security

File Integrity Monitoring: Catching Compromise in Hours, Not Months

Compromised WordPress sites are rarely discovered the day they get hacked. Most owners find out weeks or months later, usually when Google flags the site, a host suspends the account, or traffic quietly drains away. The gap between infection and discovery is where the real damage happens: spam injected into search results, malware served to visitors, or a backdoor sitting quietly until it’s needed. File integrity monitoring closes that gap. It will not stop an attacker from getting in, but it will tell you within hours that something changed, which is the difference between a quick cleanup and a months-long mess.

What file integrity monitoring actually checks

The idea is simple: take a cryptographic hash of every file that should not normally change, store those hashes somewhere safe, and periodically compare the live files against that baseline. If a hash changes and you did not make that change, you have a signal worth investigating.

WordPress core is the easiest place to start because it is the most predictable. Core files should match exactly what ships in the official release. wp-cli has a built-in command for this:

wp core verify-checksums

This pulls the official checksums for your installed version and compares them against the files on disk. A clean site returns nothing. Any mismatch, missing file, or unexpected extra file gets reported by path. Run it after every core update to confirm the update applied cleanly, and run it on a schedule (daily or weekly, depending on how sensitive the site is) to catch tampering between updates.

It’s worth noting what this command does not cover: plugins, themes, uploads, and anything in wp-content. That’s intentional. Core has a known-good reference to check against; the rest of your site does not, unless you build one yourself.

Building a baseline for wp-content

Plugins and themes don’t have an official checksum registry the way core does, so you have to create your own known-good snapshot. A simple approach uses standard Unix tools alongside wp-cli.

First, generate a baseline of file hashes for the directories that matter most:

find wp-content/plugins wp-content/themes wp-content/mu-plugins -type f -name "*.php" -exec sha256sum {} \; > /path/to/secure/baseline.sha256

Store that baseline file somewhere outside the web root, ideally off the server entirely (a private git repo, a separate storage bucket, or your local machine). If the baseline lives next to the files it’s checking, an attacker who compromises the site can simply update both.

To check for drift later, compare current hashes against the stored baseline:

sha256sum -c /path/to/secure/baseline.sha256 --quiet

Anything that fails this check has changed since the baseline was taken. That’s your list of files to investigate first.

Update the baseline deliberately, right after you install or update a plugin or theme, not automatically on a timer. If the baseline regenerates on its own schedule, it will happily absorb a malicious change as if it were legitimate.

Uploads deserve special attention

The wp-content/uploads directory is where a huge share of real-world compromises hide, precisely because nobody expects executable code there. Uploads should contain images, documents, and media, not PHP. A quick, high-signal check is simply looking for PHP files where none should exist:

find wp-content/uploads -type f -name "*.php"

If this command returns anything at all, treat it as a serious finding. Legitimate plugins essentially never write executable PHP into uploads. This single check catches a large share of the web shells and backdoors that get dropped through vulnerable upload forms or compromised plugins.

Separating real signals from noise

The biggest reason file integrity monitoring gets abandoned is alert fatigue. If every plugin auto-update, cache rebuild, or theme customizer save triggers a flood of “changed file” notifications, people stop reading them. A useful monitoring routine needs to filter deliberately.

  • Expect changes after updates. Re-baseline immediately after you update or install anything. A diff that appears right after a plugin update you initiated is not a signal, it’s expected.
  • Exclude known-noisy paths. Cache directories, log files, and anything generated by page builders or form plugins at runtime will change constantly and are not worth hashing. Scope your baseline to code files (PHP, JS) in plugins, themes, and mu-plugins, not the whole filesystem.
  • Watch timing, not just content. A file that changes at 3 a.m. when nobody on your team was working is a stronger signal than one that changed during a deploy window.
  • Prioritize new files over modified ones. A brand-new PHP file appearing in an unexpected location (a theme’s uploads folder, a plugin directory that hasn’t been touched in months) is usually a more urgent finding than a one-line change inside an existing file.

The goal is a short list of high-confidence findings you’ll actually look at, not a long log you’ll eventually ignore.

What a real compromise tends to look like

When integrity monitoring does catch something, common patterns include: a new admin user created outside your normal workflow (check with wp user list --role=administrator), obfuscated code using functions like base64_decode or eval stuffed into an otherwise legitimate-looking theme file, a new file in wp-content/uploads with a .php extension, or a core file that no longer matches its checksum. None of these are proof on their own, but any of them combined with an unexplained file change is worth pulling the thread on.

Making this sustainable

Manually running these commands works, but the value comes from consistency. Wire the checks into a cron job that emails or pings you on failure, or fold them into your deployment pipeline so a baseline refresh happens automatically right after every deliberate change. Some managed hosts, ServerBorn included, run file integrity checks as part of the platform so this happens without you maintaining the scripts yourself, which is worth knowing if you’d rather not own the tooling.

Either way, the underlying principle doesn’t change: known-good hashes, a baseline stored somewhere an attacker can’t touch, and a habit of checking regularly. That’s what turns a months-long undetected compromise into a same-day cleanup.

Takeaway

File integrity monitoring is not glamorous, but it is one of the highest-value security habits available to a WordPress site owner. Use wp core verify-checksums for core, build your own hashed baseline for plugins and themes, treat any PHP file in uploads as suspicious by default, and filter your alerts so real signals don’t get lost in routine noise. Detection speed is the whole game: the sooner you know something changed, the smaller the cleanup.