Description
SMEPlan Security Shield is a free, open-source security plugin built around a practical WordPress operations checklist: it watches the 3 most common attack surfaces (OWASP-class attacks plus WordPress-specific ones, persistence mechanisms, and entry vectors), detects issues with baseline/checksum + signature + thresholded heuristics, and remediates safely (quarantine instead of outright deletion; 1-click rollback).
Key features
- Batched, checkpointed file scanning: prioritizes
mu-plugins, drop-ins, the active theme/plugins, anduploads; never loads the whole file tree into RAM at once. - Safe database scanning: keyset pagination (no
OFFSET) overoptions/posts/postmeta; only flags a row when it decodes into an actually executable PHP/JS token, skipping image data URIs. - Configuration checks:
.htaccess/.user.inirules that map media extensions to PHP,auto_prepend_file, file/directory permissions, weak salts/keys, unusual cron entries. - Baseline/integrity: compares core files against WordPress.org’s official checksums; automatically builds a SHA-256 baseline for every plugin/theme on install/update; 1-click restore of any mismatched core file from a signature-verified WordPress.org package, with the current file quarantined first.
- Quarantine & rollback: moves suspicious files aside (never deletes), with a full transaction log and 1-click rollback.
- Per-component backups: a “last confirmed good” snapshot of each plugin/theme, refreshed on every trusted update, checksum-verified before every restore, with a best-effort local tamper-resistance layer.
- Maintenance mode with a TTL that turns on automatically when remediation touches a hot path or a large batch, and turns itself off once a health-check passes.
- Hardening: login rate-limit/lockout by IP + IP/username (real IP behind a CDN via trusted proxies), disables XML-RPC pingback + caps
system.multicall, security headers (HSTS/X-Frame-Options/CSP Report-Only), controlled auto-updates (low-traffic time window, skips VCS-managed sites, health-check after updating). - Multi-layer scan scheduling: WP-Cron + an internal watchdog + an HMAC-signed REST endpoint (for system cron/remote pings) + a WP-CLI command — the schedule keeps running even when WP-Cron is unreliable.
- Multisite: enumerates every site by
blog_id, scanning each site’s ownuploadsfolder and tables.
Two hardening behaviours worth knowing about before you enable them, because they change how the site answers requests that are not this plugin’s own:
- User-enumeration blocking is on by default. For visitors who are not signed in, the core
wp/v2/usersREST routes stop being served and?author=<id>links redirect to the home page. This is a deliberate part of the login-hardening layer, but it is a change to an API this plugin does not own — a headless front end, a mobile app or a third-party integration that reads the public user list will see it disappear. Turn it off under Hardening if something depends on it. (rc-47) wp smeplan-ss scan runexits 75 when a scan is already running. 75 isEX_TEMPFAIL— “temporary failure, try again” — rather than 0, so a wrapper running underset -ewill treat a busy lock as a failed command. Handle 75 explicitly if you schedule the command that way. (rc-49)
Not yet in this release (planned for later versions)
- 2FA (TOTP) and CAPTCHA for the login page.
- Anonymous telemetry (opt-in).
- Translations (every string is already wrapped in
__(), ready for translators via translate.wordpress.org — no translation is bundled with the plugin itself). - Action Scheduler integration for enterprise-grade durable queuing.
Privacy Policy
By default, this plugin does not send any data outside of the site it is installed on. Everything it collects (scan findings, logs, baseline data) stays in the local WordPress database and in a protected local storage folder inside the uploads directory (wp-content/uploads/smeplan-security-shield/, blocked from direct web access).
Two features send data off-site, and both are entirely opt-in — off unless the site admin explicitly sets them up:
- Alert email: if enabled, a summary of new findings is emailed to the site’s configured admin email address (
admin_email) using WordPress’s ownwp_mail(). - Alert webhook: if the admin enters a Webhook URL in Policies, a summary (site URL, alert subject, malicious/suspicious counts, timestamp — no personal or visitor data) is sent as JSON to that admin-provided URL whenever new findings are detected. Nothing is sent anywhere unless the admin fills in this field themselves.
That storage folder outlives the plugin on purpose: deleting the plugin removes its options, cron events and capabilities, but leaves the folder in place so a quarantined file is never destroyed by an uninstall performed mid-incident. See the FAQ entry “What is removed when I delete the plugin?” for the reasoning and for how to remove it yourself.
The plugin does not phone home to any SMEPlan-operated server, does not track usage/analytics, and does not include any third-party tracking or advertising code.
Installation
- Upload the plugin to
wp-content/plugins/or install it through the Plugins screen. - Activate the plugin.
- Go to Security Shield Wizard to check WP-Cron/loopback and configure trusted proxies if the site sits behind a CDN.
- See Security Shield Policies to turn hardening features on/off as needed.
FAQ
-
Does the plugin delete suspicious files automatically?
-
No. Files at the “malicious” level are moved into quarantine (
wp-content/uploads/smeplan-security-shield/quarantine/) rather than deleted, and can be rolled back with 1 click. Files at the “suspicious” level are only recorded, waiting for manual review on the Findings page.Quarantined files are kept for a limited time: once a session is older than the retention period set under Policies (14 days by default), it is removed automatically to stop the quarantine folder growing without bound. Roll back anything you want to keep before that window closes, or raise the retention setting.
-
What is removed when I delete the plugin?
-
Deleting the plugin removes every option it created, its scheduled events, and the three custom capabilities it grants. It deliberately does not remove its storage folder at
wp-content/uploads/smeplan-security-shield/, which holds the quarantine, the file baseline, the event log and any component backups.This is a deliberate choice, not an oversight. Quarantined files are moved, not copied — the folder holds the only remaining copy of anything the plugin took out of the site. Deleting a plugin is easy to do in the middle of handling an incident, or by someone who is not the person investigating, and having that click silently destroy both the only rollback path and the only forensic evidence would be the wrong default for a security plugin. Reinstalling brings the previous quarantine and logs straight back.
To remove it, delete that folder yourself over SFTP or your host’s file manager once you are sure nothing in it is still needed. Read the caution first: the
quarantine/subfolder can contain live malicious files that were pulled off the site. Delete them, do not move them back into place. -
Does the plugin automatically edit database content or the .htaccess file?
-
No. Every finding in the database or in configuration files (.htaccess/.user.ini) is only reported, never auto-fixed, to avoid breaking a site’s legitimate functionality.
-
Does this plugin change how WordPress updates itself?
-
No. It never supplies its own update source: there is no bundled update checker, no third-party update server, no filtering of the plugin-information API, and nothing written to the transients core caches available updates in. Everything WordPress installs still comes from WordPress.org, fetched and verified by WordPress itself.
What it does offer — off by default, and only if you switch it on in Policies — is control over when an update WordPress has already found and verified gets applied. Using core’s own public
auto_update_core/auto_update_plugin/auto_update_themefilters, it can hold an update back until the low-traffic window you configure, skip it while the site is under load, and skip any plugin or theme directory that is under version control (updating those breaks a deploy). It only ever delays WordPress’s own updates; it never substitutes them.Since 0.7.32 it can also refuse one: a plugin or theme package is scanned while still in its temporary folder, and if a file in it matches malware detection the install is stopped before anything is written. A manual update can be allowed through once from the dashboard notice; an automatic one is refused and reported. This uses core’s own
upgrader_source_selectionfilter, changes nothing about where updates come from, and can be switched off in Policies.Automated scanners flag any use of those filters for a human to look at, which is why this is spelled out here.
-
What environment does it need?
-
WordPress 6.1+, PHP 7.4+. Works best when WP-Cron runs normally; if the site has
DISABLE_WP_CRONset or blocks loopback requests, use system cron/WP-CLI following the instructions on the Wizard page.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“SMEPlan Security Shield” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “SMEPlan Security Shield” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
Newest release only — WordPress.org truncates this section at 5,000
characters. The full history is in changelog.txt, shipped with the plugin.
0.7.35
Plugins and themes stop being taken purely on trust.
- Plugins are now checked against WordPress.org’s official per-file checksums. Until this release every integrity check compared the site against ITSELF: a baseline records what a component looked like the first time it was seen, which catches later tampering and says nothing at all about a plugin that was already backdoored before it was seen. WordPress.org publishes a per-file md5 and sha256 for every plugin in the directory, and that answers a question no local history can — are these the bytes the author shipped? Files that were changed are reported, and so are files that are not part of the official release at all, which is how most backdoors actually arrive: they add a file rather than edit one. (rc-80)
- Two separate columns, because they answer different questions. “Matches published files” is about provenance. “Upstream” is about whether that release is still looked after — including the case that matters most, a plugin removed from the directory, which is often removal because of a security problem. A plugin can be perfectly authentic and abandoned three years ago; one combined badge would hide exactly that pairing. Premium and custom plugins have no upstream to compare against and now say so plainly instead of showing a neutral tick. (rc-80)
- Trust is no longer permanent. Every baseline, every accepted file and every backup now records WHICH generation of the detection rules vouched for it. A payload the rules of the day scored below the threshold gets written into a baseline as the known-good state, and from then on it is the definition of correct for that file — re-running the same rules forever changes nothing. When the rules improve, everything carrying an older stamp is examined again with the detector as it is now, so improvements reach backwards instead of only applying to files first seen afterwards. (rc-79)
- An accepted file is still a human decision. A rules bump re-scores acceptances, but only a malicious verdict overrides the person who accepted it; anything milder leaves the decision standing. Dumping previously triaged findings back on the admin twice is how a Findings screen stops being read. (rc-79)
- A backup taken under older rules is re-checked before it is restored. Backups are scanned before being zipped, but with the detector of the day — so an archive can hold a payload those rules did not recognise, and “Restore from backup” would put it straight back. Such an archive is now unpacked to a temporary directory and run through the current rules first, and refused with the offending path named if it fails. (rc-79)
- Baselines record the component’s own version. Every automatic caller used to stamp them with the scanner’s version instead, so a baseline for WooCommerce 8.4 said “0.7.32”. That made the field useless for its one purpose and made an upstream lookup impossible, since the checksum file is addressed by the component’s own version. (rc-79)
Cost: one outbound request per plugin per day, cached hard, on its own cron lane with a queue — never inside a scan tick, where it would run straight into the per-tick budget.
Older releases: see changelog.txt.

