Skip to content
WordPress.org
  • Showcase
  • Plugins
  • Themes
  • Hosting
  • News
    • Learn WordPress
    • Documentation
    • Education
    • Forums
    • Developers
    • Blocks
    • Patterns
    • Photos
    • Openverse ↗︎
    • WordPress.tv ↗︎
    • About WordPress
    • Make WordPress
    • Events
    • Five for the Future
    • Enterprise
    • Gutenberg ↗︎
    • Job Board ↗︎
  • Swag ↗︎
  • Get WordPress
Get WordPress
WordPress.org

Plugin Directory

Kwistech Secure

  • Submit a plugin
  • My favorites
  • Log in
  • Submit a plugin
  • My favorites
  • Log in

Kwistech Secure

By kwistech
Download
  • Details
  • Reviews
  • Installation
  • Development
Support

Description

Kwistech Secure hardens a WordPress site using only what WordPress already gives you. It is
not a firewall, not a malware scanner, and not a service — everything it does happens on your
own server, and it never contacts anybody.

Twelve controls, each independently switchable, each logged when it changes:

  • Two-factor sign-in — RFC 6238 authenticator codes with one-time recovery codes. Detects
    an existing 2FA plugin and stays out of the way rather than fighting it.
  • Login rate limiting — counts failures by IP address and by username, so a password
    sprayed across many accounts from one host and one account attacked from a botnet are both
    caught. Usernames are stored only as a salted hash.
  • Non-enumerating login errors — one identical message for every failure, on the sign-in
    form and the password-reset form alike, so neither confirms which usernames exist.
  • Custom sign-in address — moves /wp-login.php. Tests the new address before saving it
    and provides a break-glass constant, because the obvious way to implement this is also the
    obvious way to lock yourself out.
  • File editor disabled — removes the most direct path from a stolen administrator session
    to arbitrary code execution.
  • XML-RPC off — closes a long-standing brute-force amplifier that most sites never use.
  • Application password inventory — reports how many exist per user, never their values.
    They bypass two-factor, and most people have forgotten the ones they created.
  • Version fingerprints removed — generator tags, feed generators, X-Powered-By.
  • Username discovery blocked — ?author=1 scans, the REST user list, the user sitemap
    and oEmbed author fields all stop answering anonymous callers. Author archives at their own
    address keep working, and so does everything a signed-in editor needs. The control also
    reports when an account’s display name is its username, because when that is true the
    byline gives the game away regardless.
  • Security response headers — X-Content-Type-Options, X-Frame-Options,
    Referrer-Policy and X-Permitted-Cross-Domain-Policies, and never overwriting one your
    server or CDN already sends. Deliberately no CSP: any policy that leaves wp-admin usable
    needs unsafe-inline, which does not stop the attack CSP exists to stop.
  • Enforced password policy — a minimum length, and a refusal of passwords containing the
    username or the site name, applied server-side when a password is set or reset. WordPress
    itself only shows a strength meter next to a “confirm use of weak password” checkbox.
  • unfiltered_html restricted — via a capability filter rather than the all-or-nothing
    constant, so administrators keep the workflows they need.

An append-only activity log records every setting change with the old value, the new
value, who made it, from what address, and when. There is no edit path and no single-row
delete; the only removal is the scheduled retention sweep, which logs itself.

What it does not do

Being clear about this matters more than the feature list:

  • It does not scan for malware, and a clean install proves nothing about whether you are
    infected.
  • It does not filter traffic. It is not a WAF and cannot stop an attack in progress.
  • It does not patch anything. It reports on outdated core and plugins; updating is yours.
  • It cannot protect against someone who already has your administrator password and your
    second factor.

Privacy

This plugin makes no third-party network request of any kind. There is no account, no
registration, no telemetry, no usage reporting, no licence check, and no remote access. It
does not phone home because there is nowhere for it to phone.

Two requests are made, both to your own website address: an HTTPS request to your home page
to check whether a security header is present, and a one-off request to a new sign-in address
to confirm it works before saving it. Neither carries anything about you.

Data stored, all locally: the activity log (400 days), failed sign-in attempts with usernames
hashed (30 days), and encrypted two-factor secrets in user meta until you turn the feature
off. Deleting the plugin removes all of it.

Optional paid add-on

A separate plugin, Kwistech Secure — Insurance Evidence Pack, generates a dated PDF and
JSON report of this site’s security posture for an insurance broker. It is sold at
kwistech.ca and is not required for anything here.

Everything described on this page is free, works without it, and always will. This plugin
contains no locked functionality.

Screenshots

Installation

  1. Upload the plugin through Plugins → Add New → Upload Plugin, or install it from the directory.
  2. Activate it. Ten of the twelve controls switch on immediately at safe defaults.
  3. Open Kwistech Secure → Hardening and decide on the two that need a decision from you: the custom sign-in address and two-factor enforcement. Both default to off, deliberately — enabling either without preparation can lock you out of your own site.

No configuration file to edit, no keys to obtain, no account to create.

FAQ

Will this lock me out?

Two controls can, and both are built to make that hard. The custom sign-in address makes a
real request to the new address before saving it, and refuses to save if it does not answer;
if the check cannot run, you must tick an acknowledgement, and that choice is recorded.
Two-factor enforcement cannot be switched on until at least one administrator has enrolled,
because requiring codes before anyone has an authenticator locks out everybody at once.

If you are locked out anyway, add define( 'KWISTECH_SECURE_DISABLE_LOGIN_SLUG', true ); to
wp-config.php and /wp-login.php works again immediately.

Does it conflict with my existing security plugin?

It detects other two-factor plugins and steps aside rather than running a second
implementation. For firewall and scanning plugins there is no overlap — this one does neither.
Switch off any individual control that duplicates something you already have.

Does it slow the site down?

The hardening controls are filters and capability checks with no database work on a normal
page load. Nothing runs on the front end except the header changes.

Does it work on multisite?

The code paths exist and have never been run on a real network. Treat multisite as
unsupported rather than broken.

What happens to my data if I uninstall it?

Deleting the plugin removes its options, both database tables, the scheduled job, and every
user meta key it set, including two-factor secrets and recovery codes. Deactivating changes
nothing. Constants you added to wp-config.php by hand are left alone, because this plugin
never edits that file.

Is the source readable?

Yes, deliberately. No minified files, no bundled dependencies, no build step, no obfuscation.
A security plugin you cannot read is a security plugin you cannot check.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Kwistech Secure” is open source software. The following people have contributed to this plugin.

Contributors
  • kwistech

Translate “Kwistech Secure” into your language.

Interested in development?

Browse the code, check out the SVN repository, or subscribe to the development log by RSS.

Changelog

1.6.0

  • Every option, user meta key, database table and scheduled job this plugin stores has moved
    from the kws_ prefix to kwistech_secure_. Three characters is below the four-character
    minimum the plugin directory asks of a prefix, and it was the one part of the plugin still
    using the short form. Your data is migrated automatically the first time an administrator
    loads a wp-admin page after upgrading: settings, audit log, login history, two-factor
    enrolment and recovery codes all carry across. Tables are renamed rather than copied, so no
    audit entry is lost.
  • If you read this plugin’s data directly — a WP-CLI script, a dashboard widget, a database
    query — update it to the new names. kws_settings is now kwistech_secure_settings, the
    audit table is now {prefix}kwistech_secure_audit_log, and the daily job is now
    kwistech_secure_daily_maintenance.
  • The key used to encrypt two-factor secrets at rest is rotated as part of the same change.
    This is transparent: existing secrets are still read under the previous key, so your
    authenticator app keeps working and there is nothing to re-enrol.
  • Removed the cleanup that deleted two retired database tables and a retired encrypted
    credential left over from a payment feature that was never released. No installation has
    ever held that data, so the code deleted something that could not exist.
  • The add-on notice introduced in 1.5.0 is now four lines instead of twelve, and appears only
    on this plugin’s dashboard rather than on all four of its screens.
  • Reading a submitted form field now requires that the capability and nonce check has already
    passed, enforced in code rather than by convention. No behaviour changes for anyone using
    the plugin normally; it closes the gap where a handler added in future could forget the
    check.
  • Requires the Insurance Evidence Pack add-on 1.2.0 or newer, if you use it. Older add-on
    versions keep working but will not have their own data migrated.

1.5.0

  • Added a one-off notice on this plugin’s own screens introducing the Insurance Evidence Pack
    add-on. It appears nowhere else in wp-admin, never appears if the add-on is installed, is
    dismissed permanently and per-user, is styled as information rather than as a warning, and
    loads nothing from anywhere. Developers and agencies can switch it off with the
    kwistech_secure_show_addon_notice filter.
  • Fixed a test-isolation bug where the active-plugin cache leaked between unit tests.

1.4.0

  • Three new hardening controls, taking the total to twelve. All three default to on; none
    of them can lock you out.

    • Block username discovery. Closes ?author=N scans, /wp-json/wp/v2/users, the user
      sitemap and oEmbed author fields to anonymous visitors. This was a known gap, recorded in
      the threat model as accepted residual R-5. Refusing to confirm usernames at the sign-in
      form while handing the list out over REST was never a coherent position.
    • Security response headers. Four of them, and it will not overwrite a header your server
      or CDN already sends.
    • Enforced password policy. Minimum length, and no passwords containing the username or
      the site name. Separators and case are ignored on both sides of that comparison, so
      “Test-Site-2026” is caught for a site called “Test Site”. Existing passwords are stored as
      hashes and cannot be assessed; the policy applies from the next time each one is set.
  • Added a regression test that the hardening form and its save handler agree on the field
    list. An unchecked checkbox submits nothing, so a field rendered but not read back is
    silently switched off on every save — a bug that only surfaces as “this setting will not
    stay on”, and precisely the mistake a three-control release invites.

1.3.0

  • Renamed from “Kwistech WP Secure” to “Kwistech Secure”. “WP” is a restricted term that
    cannot appear in a plugin name or slug, so the old name could never have been published.
    The plugin folder is now kwistech-secure; if you installed 1.2.0 by hand, delete the old
    kwistech-wp-secure folder after upgrading or WordPress will run both copies at once.
    Settings, audit log and login history are untouched — they are keyed independently of the
    folder name and carry across.
  • Extension hooks renamed to match the new slug: kws_dashboard_cards,
    kws_settings_panels, kws_admin_notices and kws_admin_notice_messages become
    kwistech_secure_*. The opt-out constant KWS_DISABLE_LOGIN_SLUG becomes
    KWISTECH_SECURE_DISABLE_LOGIN_SLUG. Update the add-on to 1.1.0 at the same time.
  • Now requires WordPress 6.2, up from 6.0. Table names in the audit log and login-attempt
    queries are passed through $wpdb->prepare() as %i identifier placeholders rather than
    interpolated into the SQL string. The old form was safe — the names were built from
    $wpdb->prefix plus a literal — but “safe because you can read the surrounding code”
    is weaker than “cannot be otherwise”, and it needed a static-analysis suppression on every
    query to say so. %i arrived in WordPress 6.2.

1.2.0

  • Split into two plugins. The evidence pack is now a separate paid add-on distributed from
    kwistech.ca; this plugin contains no locked functionality and never will.
  • Added extension points so add-ons attach through documented hooks rather than patching.
  • Settings screen now explains what is stored and for how long, instead of offering a
    retention control that governed data this plugin no longer owns.
  • Renamed for clarity. The plugin folder and all settings are unchanged; nothing to migrate.

1.1.0

  • Removed the payment layer entirely. No webhook, no outbound third-party request, no stored
    credential.
  • Every entry point is now a capability-checked, nonce-protected wp-admin request.

1.0.1

  • Fixed admin styles being served from cache after an update.

1.0.0

  • Initial release.

Meta

  • Version 1.6.0
  • Last updated 21 hours ago
  • Active installations Fewer than 10
  • WordPress version 6.2 or higher
  • Tested up to 7.1
  • PHP version 7.4 or higher
  • Tags
    audit loghardeninglogin securitysecuritytwo factor
  • Advanced View

Ratings

No reviews have been submitted yet.

Your review

See all reviews

Contributors

  • kwistech

Support

Got something to say? Need help?

View support forum

  • About
  • News
  • Hosting
  • Privacy
  • Showcase
  • Themes
  • Plugins
  • Patterns
  • Learn
  • Documentation
  • Developers
  • WordPress.tv ↗
  • Get Involved
  • Events
  • Donate ↗
  • Swag ↗
  • WordPress.com ↗
  • Matt ↗
  • bbPress ↗
  • BuddyPress ↗
WordPress.org
WordPress.org
  • Visit our X (formerly Twitter) account
  • Visit our Bluesky account
  • Visit our Mastodon account
  • Visit our Threads account
  • Visit our Facebook page
  • Visit our Instagram account
  • Visit our LinkedIn account
  • Visit our TikTok account
  • Visit our YouTube channel
  • Visit our Tumblr account
Code is Poetry
The WordPress® trademark is the intellectual property of the WordPress Foundation.