=== EDZNET Security Guard ===
Contributors: edznet
Donate link: https://edznet.dev/
Tags: security, admin, brute force, login, hardening
Requires at least: 5.8
Tested up to: 7.1
Requires PHP: 7.2
Stable tag: 1.4.2
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Requires 2FA for administrators, blocks new admin creation, refuses URL-credential logins, and emails you on admin logins. Locked to first admin.

== Description ==

EDZNET Security Guard stops the most common takeover techniques seen on compromised WordPress sites:

### Two-Factor Authentication

Require **two-factor authentication (2FA)** for administrator logins using the TOTP standard (RFC 6238) and any authenticator app — Google Authenticator, Authy, 1Password, Microsoft Authenticator, or similar. Setup is guided: the administrator scans a QR code from their profile or the login page and enters a 6-digit code that changes every 30 seconds. The code is verified on the server against an encrypted secret, and after a successful 2FA login the normal "admin logged in" email is still sent.

You can require 2FA for all administrators, force new administrators to set it up, or force every administrator to enroll on their next login. Existing administrators are never locked out — anyone who hasn't set it up yet is walked through enrollment before they can finish logging in.

### Block Administrator Creation

A compromised admin account will often create a second administrator account as a backdoor — one that survives after the original breach is cleaned up. While this setting is enabled, **no new user can be created with the Administrator role and no existing user can be promoted to one**. Existing administrators are unaffected, so the first administrator can still manage the site and change the setting whenever a legitimate admin account is needed.

### Block URL Credentials

Brute-force scripts frequently hammer `wp-login.php` with credentials baked into the URL, e.g. `https://yoursite.com/wp-login.php?log=Ben&pwd=T3m`. While enabled, **any visit to the login page with credentials in the query string is refused with `403 Forbidden`**. All logins must use the normal login form.

### Admin Login Alerts

Every time an **administrator** signs in, an email is sent with the username, the IP address they connected from, their browser, and the exact time. Legitimate admin logins are rare, so an alert for a login you didn't make is an early warning that an account may be compromised. Alerts are on by default and go to the first administrator's email address — you can point them at any address from the settings page.

### Block Accounts

Block any account from logging in with a single click — the "Block login" action appears on the Users screen, and "Unblock login" restores it whenever you're ready. A blocked account is refused at the login screen with a friendly message, and its existing sessions are ended immediately so it can't stay signed in. Because the plugin is built around the first-administrator lock, it can also block **administrator** accounts: any administrator can block a non-admin account, while only the first administrator can block another administrator. The first administrator's own account can never be blocked, so you can't lock yourself out of the site.

Every attempt to sign in as a blocked account is logged — username, IP address, browser and time — on the EDZNET Guard settings page, so you can see who keeps trying.

### First Administrator Lock

The same first-administrator lock applies to every setting in the plugin — including two-factor authentication — so a compromised admin account cannot quietly weaken the protections.

### Comment Controls

The same first-administrator lock protects three comment tools:

- **Disable comments globally** — turn commenting off site-wide with a single toggle. Comment forms disappear from the front end and newly published posts default to closed comments.
- **Force close comments** — one click updates every existing post so comments and pingbacks are closed, without having to edit posts one by one.
- **Delete all pending comments** — a danger action that permanently deletes every comment still awaiting moderation, without first marking each as spam or trashing it. Confirmation is required before it runs.

### Front-end Content Tuning

Hide what you don't want visitors to see, with each toggle independent of the others:

- **Hide post authors** — removes the "by [author]" meta from your posts.
- **Hide post dates** — removes publish dates from your posts.
- **Hide comment closed text** — removes the "Comments are closed" / "Comments Off" wording that appears under posts where commenting is disabled.

These are quick front-end filters and CSS rules that work with most themes.

### Go Further: EDZNET Geolocation

Security Guard pairs naturally with our **EDZNET Geolocation** plugin. Once installed, block logins, comments and whole countries by visitor location, block individual IPs, and see where your visitors come from — a useful layer on top of the protections here. Security Guard detects the plugin and links straight to its country-blocking screen. You can grab it from WordPress.org: https://wordpress.org/plugins/edznet-geolocation/

The block, the alert, and the comment and content toggles are on/available by default and need no configuration after activation. Two-factor authentication is off by default — turn it on from the settings page (you'll be prompted to set up your own code first).

== Installation ==

1. Upload the `edznet-security-guard` folder to `/wp-content/plugins/`.
2. Activate the plugin through the Plugins screen in WordPress.
3. The protections are now active. Review them under **EDZNET Guard** in the admin menu.

== Source Code ==

This plugin is distributed as complete, human-readable source. Every PHP, JavaScript and CSS file shipped in the plugin ZIP is the original source file — nothing is minified, compiled, obfuscated, transpiled or bundled, and there is no build step, package manager or build tool of any kind involved in producing a release. What you download is what was written.

Runtime CSS lives in `css/` and runtime JavaScript in `js/`, both loaded through `wp_enqueue_style()` / `wp_enqueue_script()`. The only third-party file is the QR-code library credited below, which is also shipped as its original unminified source.

== Third-Party Libraries ==

* **qrcode.js** (`js/qrcode.js`) — by davidshimjs, based on the "QR Code for JavaScript" library by Kazuhiko Arase. Licensed under the MIT license. Upstream: https://github.com/davidshimjs/qrcodejs. Shipped unmodified and unminified; used to draw the TOTP enrollment QR code in the browser so that no secret ever leaves the site.

== No External Services ==

This plugin does not contact any external service. Two-factor codes are generated and verified entirely on your own server, the QR code is drawn in the browser by the bundled `qrcode.js`, and no data about your site, its users or its visitors is transmitted anywhere.

== Frequently Asked Questions ==

= Which account can change the settings? =

The site's first administrator account — the user with the Administrator role and the earliest registration date (registration dates are not changed when an account is renamed). If that account is deleted, the lock moves to the next earliest-registered administrator.

= I'm an admin but the settings are locked. Why? =

Only the first administrator account can change these settings. This is intentional: a compromised admin account should not be able to disable the protections.

= Can the first administrator create new admins? =

Yes — temporarily disable "Block administrator creation", create the account, then re-enable it.

= Does this work with REST API or XML-RPC user creation? =

Yes. The block is enforced at the core `wp_insert_user()` level, so admin-created users, REST API calls, and XML-RPC requests are all refused while the setting is enabled.

= What triggers an admin login alert? =

Any successful sign-in by a user with the Administrator role — including the first administrator. Each alert includes the username, IP address, browser, and the time of the login. Alerts are sent to the first administrator's email unless you set a different address on the settings page.

= How does two-factor authentication work? =

Once enabled, an administrator enters their username and password as usual, then a 6-digit code from an authenticator app (Google Authenticator, Authy, 1Password, Microsoft Authenticator, and any other TOTP-compatible app). The code is generated from a secret that is only shown once, as a QR code or a text key, and verified server-side against that secret. Codes refresh every 30 seconds.

= Where does an administrator set up two-factor authentication? =

In their profile under "EDZNET Guard — Two-Factor Authentication", or on the login page itself: an administrator who hasn't set up 2FA yet is shown the enrollment screen at their next login instead of being signed straight in.

= I lost my phone / authenticator app. How do I sign in? =

The first administrator can open the administrator's profile and reset (remove) their two-factor authentication, then they sign in again and set up a new secret. If the locked-out administrator IS the first administrator, the secret can be removed with one click from their own profile by an administrator who has already completed 2FA — or, if nobody can get in, the two-factor authentication can be turned off directly in the database by deleting the `edznet_guard_2fa_enabled` option.

= Does this plugin offer geo-blocking? =

Not built in — but it pairs with our **EDZNET Geolocation** plugin, which blocks logins, comments and whole countries by visitor location, blocks individual IPs, and shows where your visitors come from. When Geolocation is active, Security Guard links straight to its country-blocking screen. Install it from WordPress.org.

= How do I block a user from logging in? =

On the Users screen, hover over a user and click **Block login**. The account is refused at the login screen and its existing sessions are ended. Click **Unblock login** (or the button on the EDZNET Guard settings page) to restore it at any time.

= Can I block an administrator? =

Yes. Any administrator can block a non-admin account; only the site's first administrator can block another administrator. The first administrator's own account can never be blocked, so the site owner can't be locked out.

= Can I see who tried to log in with a blocked account? =

Yes. Every attempt is recorded on the EDZNET Guard settings page under **Blocked login attempts** — username, email, IP address, browser and time, newest first (up to the last 100).

= Can I turn off the admin login alerts? =

Yes. Untick "Email me when an administrator logs in" on the EDZNET Guard settings page. The toggle is locked to the first administrator like the other settings.

= Who can use the comment controls? =

Only the site's first administrator. "Disable comments globally" is a protected setting, and the "Close commenting on all posts" and "Delete all pending comments" buttons are locked the same way so a compromised admin account cannot purge comments or weaken moderation.

= Does disabling comments globally delete existing comments? =

No. It only hides comment forms and makes new posts default to closed. To change existing posts, use "Close commenting on all posts". To remove comments that are waiting for moderation, use "Delete all pending comments".

= How do the "hide author", "hide date" and "hide comment closed text" toggles work? =

They are front-end filters and CSS rules. Hidden content is removed from the markup the theme outputs, and a small stylesheet hides common author/date/comment-closed elements. Because themes structure their post meta differently, these are a best-effort helper for most themes rather than a guarantee for every theme.

= Do the front-end toggles affect the admin? =

No. Hiding post authors, dates and comment-closed text only affects the front end of the site. The admin screens always show full author and date information.

== Changelog ==

= 1.4.2 =
* Removed the site-wide "EDZNET Geolocation" admin notice. The companion-plugin recommendation now appears only on the plugin's own EDZNET Guard settings page, so no other admin screen is touched.
* All admin and login-screen CSS moved out of PHP into `css/admin.css` and `css/login.css`, and all JavaScript into `js/qr-init.js` and `js/profile-2fa.js`, loaded via `wp_enqueue_style()` / `wp_enqueue_script()`. No inline `style` attributes remain in the plugin's markup.
* Hardening: every `admin_post` handler (block/unblock account, clear blocked-login log, two-factor setup/removal) now verifies the nonce first and then re-checks `current_user_can( 'manage_options' )` explicitly, instead of relying only on the first-administrator identity check.
* The Users-screen notices are now gated on the `list_users` capability.
* Documented in the readme that the plugin ships complete unminified source with no build step, credited the bundled MIT-licensed `qrcode.js` library, and stated explicitly that the plugin contacts no external service.

= 1.4.1 =
* Security and UI fixes.

= 1.4.0 =
* Added two-factor authentication for administrators (TOTP / RFC 6238, works with Google Authenticator, Authy, 1Password and more). QR-code or text-key enrollment from the profile or the login page, optional "require for all admins", "force new admins to enroll", and "force enrollment on next login" toggles. Locked to the first administrator.
* Added account blocking: "Block login" / "Unblock login" row actions on the Users screen, able to block administrator accounts (first administrator only), with immediate session termination and a log of every attempted sign-in by blocked accounts.
* Added a recommendation for the companion EDZNET Geolocation plugin (geo-blocking by country and visitor tracking), with direct links to its blocking screen once it's installed.

= 1.3.0 =
* Added admin login alerts: emails the first administrator (or a custom address) with the username, IP address, browser and time whenever an administrator signs in. On by default, locked to the first administrator.

= 1.2.0 =
* Added front-end content tuning: "Hide post authors", "Hide post dates" and "Hide comment closed text" toggles, each independent and locked to the first administrator.

= 1.1.0 =
* Added "Disable comments globally" toggle (hides front-end comment forms, new posts default to closed).
* Added "Close commenting on all posts" bulk action.
* Added "Delete all pending comments" danger action. All comment controls are locked to the first administrator.

= 1.0.0 =
* Initial release. Blocks creation/promotion of administrator accounts and refuses login attempts that pass credentials through the URL. Settings locked to the site's first administrator.
