Security Kit
Zero-admin-UI bot and brute-force protection for WordPress and WooCommerce. Honeypot, timing trap, and login lockout that covers every auth vector: wp-login, WooCommerce, XML-RPC, REST.
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/axeldegouyon/security-kit/archive/refs/heads/main.zipReadme
Security Kit
Lightweight bot and brute-force protection for WordPress and WooCommerce.
Single file, no settings screen, no dashboard footprint — every behavior is
controlled by constants at the top of security-kit.php.
What it does
- Honeypot: a hidden form field real visitors never fill in. A non-empty submission means a bot.
- Timing trap: rejects submissions that arrive faster than a human could plausibly fill the form.
- Nonce verification: on every protected form.
- Login lockout: dual-key on IP and username, checked with OR logic,
so it defends against both attack shapes: credential stuffing (many
passwords against one account, from many IPs) and password spraying (a few
passwords against many accounts, from one IP). Fires on the
authenticatefilter, so it covers wp-login.php, WooCommerce account login, XML-RPC, REST, and application passwords — not just the standard login form. - Comment cooldown: — a minimum interval between comments per identity.
- WooCommerce checkout protection: honeypot + timing on the checkout form.
- Cloudflare-aware IP resolution: only trusts the
CF-Connecting-IPheader when the direct connection genuinely originates from a published Cloudflare edge range, so the header can't be spoofed by hitting the origin directly. - Optional XML-RPC hardening: disable XML-RPC entirely, or just remove
system.multicall, the method that lets one request carry hundreds of credential attempts.
Install
Upload security-kit.php to wp-content/plugins/security-kit/ and activate.
This creates one dedicated table, wp_securitykit_state — never written to
WordPress core tables (no transients, no wp_options bloat).
Configuration
All at the top of the file:
| Constant | Default | Meaning |
|---|---|---|
SECURITY_KIT_FIELD_NAME |
sk_ref_confirm |
Honeypot field name. Change per install to something non-obvious — a guessable name (website, url) is one bots already know to skip. |
SECURITY_KIT_MIN_SECONDS |
3 |
Minimum seconds between form render and submit. |
SECURITY_KIT_LOGIN_MAX_ATTEMPTS |
5 |
Failed attempts before lockout. |
SECURITY_KIT_LOGIN_LOCKOUT |
600 |
Lockout duration, in seconds. |
SECURITY_KIT_COMMENT_COOLDOWN |
15 |
Minimum seconds between comments per identity. |
SECURITY_KIT_TRUSTED_PROXY_RANGES |
Cloudflare's published ranges | Re-check against cloudflare.com/ips periodically — Cloudflare updates this list occasionally. |
SECURITY_KIT_DISABLE_XMLRPC |
false |
Disables XML-RPC entirely. |
SECURITY_KIT_DISABLE_XMLRPC_MULTICALL |
false |
Keeps XML-RPC but removes the amplification method. |
Known trade-offs — read before relying on this alone
- The username lockout can be turned against you. Anyone who knows your admin username or email can deliberately fail 5 logins to lock your own account for 10 minutes, repeatedly. The IP counter has no equivalent downside. If that risk outweighs the protection for your site, drop the username half of the check and keep IP-only.
- This is an application-level backstop, not a wall. It still runs inside
PHP, so it still consumes a worker per attempt. Edge-level protection (a
Cloudflare rate-limit rule on
POST /wp-login.php, blocking/xmlrpc.phpoutright if you don't use it) stops traffic before it costs you anything, and is worth setting up regardless of this plugin. - XML-RPC hardening is opt-in, not on by default — enabling it will break Jetpack, the WordPress mobile app, or any remote-publishing tool that depends on it. Confirm nothing on the site uses XML-RPC before turning it on.
Extending
Every check (honeypot, timing, nonce) runs through
security_kit_validate_get_error(), and every stateful counter (lockouts,
cooldowns) runs through the four security_kit_state_*() functions backed by
the dedicated table.
A new protected form or a new counter should be built on
those existing primitives rather than introducing a new storage mechanism.