Where WordPress file integrity checks stop working

Bold text reading Hash Everything beside an isometric ledger vault stamping folders while gray folders pass unsealed.
wp core verify-checksums
Success: WordPress installation verifies against checksums.

That command is the whole idea behind a post I read recently on Towards Data Science, except WordPress has shipped a version of it for years. Sam Black walks through hashing a dataset with Python and publishing the digest on Ethereum so the record can’t be quietly rewritten, and his article is worth reading even if you never touch a chain. WordPress file integrity works the same way at a smaller scale: an authority you trust publishes a fingerprint, you compare your files against it. The command above downloads the checksums for your installed version from WordPress.org and checks every core file before WordPress even loads.

There’s a gap in this that matters for client work: the checksum authority only knows about WordPress.org.

Where WordPress file integrity checks stop working

Per the WP-CLI docs, the core command compares the files in wp-admin, wp-includes and the root against md5 checksums WordPress.org publishes for your release. Add wp plugin verify-checksums --all and you also cover every plugin with a listing in the directory. That’s a lot of surface area, and it’s the part hosts and security plugins lean on when someone suspects tampering.

It doesn’t cover premium plugins, the plugin you wrote for one client, mu-plugins, custom themes, uploads, exports, or the migration archive you hand over on a Friday. No reference exists for those, so no check exists. After a move between hosts or a long maintenance gap you can prove core is clean and say nothing at all about the code you get paid for, which is why the migration checklist I keep for client sites starts with a fingerprint of the things checksums can’t see.

That gap is what the data engineering post is about, translated to our world: a fingerprint you computed yourself, stored somewhere an accident or an attacker can’t quietly edit.

How do you hash your own files?

You don’t need a chain for the first half of this. Hash the directory, write the digest somewhere boring, compare later.

function bbioon_dir_digest( string $dir, string $algo = 'sha256' ): string {
    $ctx = hash_init( $algo );
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS )
    );

    $paths = [];
    foreach ( $iterator as $file ) {
        $paths[] = $file->getPathname();
    }
    sort( $paths ); // order matters, otherwise the digest is random

    foreach ( $paths as $path ) {
        hash_update( $ctx, str_replace( $dir, '', $path ) );
        hash_update_file( $ctx, $algo, $path );
    }

    return hash_final( $ctx );
}

Two details matter. Sorting matters because the same files visited in a different order produce a different digest, and that’s the same reason a shuffled CSV or a re-zipped folder stops matching its original hash. The relative path is in there because otherwise a renamed file is invisible. The source prefers blake2b for throughput and PHP supports it fine through hash(), but I default to sha256 because the point is that anyone can recompute the digest years later, and the folders we ship are far too small for throughput to be the constraint.

Where the digest goes is the part teams get wrong. It belongs in the deploy script output, in a MANIFEST file in the repo, and in the handoff email. The check that catches drift is the one that runs automatically at the far end. Nobody remembers to run one by hand six months later.

Is a blockchain anchor worth the trouble?

The clever part of the source is that no smart contract is involved. You send a plain transaction to your own address with the digest in the data field, sign it, broadcast it, and any block explorer will show it forever. On Sepolia, the Ethereum testnet, the ETH comes free from faucets and inclusion takes roughly 12 seconds. On mainnet the author estimates $0.04 to $0.10 per hash at $2,000 per ETH, before the gas for the transfer itself.

I’d push back on the free version. The author admits it in a footnote: testnets aren’t permanent. That makes Sepolia a notary that might reset itself, and you won’t get a notification when it does. There’s also a quieter problem. To read or write the chain you go through Infura or a similar RPC provider, which means an account and an API key, so the “no central authority” property mostly lives in the diagram. If a timestamp has to survive years, OpenTimestamps does roughly the same thing against Bitcoin, batches many digests into a single commitment, and costs nothing. That’s where I’d look before paying for mainnet gas.

For WordPress work specifically, I haven’t found a project where chain anchoring beat a signed manifest in git plus S3 Object Lock, which the source also points to for protecting the metadata record. The realistic threat on a client site is a bad deploy, a hacked server, or someone editing a file through the host’s file manager. A digest recorded in your deploy log catches all three, and a mismatch points at the exact minute something changed.

The question I still can’t answer is who verifies these records years later. A hash only proves anything if somebody recomputes it at the other end, and I’ve never had a client ask for a proof file from three years back. The checks that fire are the ones wired into deploys and staging syncs. Maybe compliance-heavy work is different, I’m not sure. If you want a digest check added to your deploy script or your staging workflow, that’s small, well-defined work I’m happy to take on.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.

Leave a Comment