| 开发者 | wpsecuredcom |
|---|---|
| 更新时间 | 2026年9月28日 18:14 |
| PHP版本: | 8.1 及以上 |
| WordPress版本: | 7.1 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
otpauth:// link, which iOS and Android hand to the installed authenticator or password app (Google Authenticator, Microsoft Authenticator, 1Password, iOS Passwords, ...); the setup key can also be copied and pasted into the app. The code field is marked as a one-time code, so iOS / macOS Passwords and password managers that hold the secret offer the current code at later logins.
On the profile page everything Secured WP adds - remembered devices, the authenticator and passkeys - is grouped in one "Secured WP" panel; the same panel is used by the [wps_custom_settings] shortcode and on WooCommerce's My Account page.
Login Attempts
This gives you the ability to prevent brute force attacks if the hacker knows the username and tries to guess the password. With this enabled, after the given amount of tries that username is locked for the given amount of time - from the address the tries came from. Somebody guessing from one place cannot lock the real user out everywhere. If the account fails ten times the allowed amount from many addresses, it is locked everywhere, except on devices the user asked to remember. Behind a reverse proxy or CDN, use the wpsec_client_ip filter to supply the visitor's real address, otherwise every visitor shares the proxy's address. Attempts are counted before the password or code is checked, with counters that cannot be outrun by sending many requests at once. An account locked by an administrator cannot be used with application passwords either.
Remember device setting
With that, user can use given device for the given amount of days without being asked for the 2FA code. The password is always required - a remembered device never signs anybody in on its own. The devices can be removed or checked from the default user settings page; changing the password or resetting the authenticator forgets them all.
That setting is based on current setting (global) for the current moment, which means that when the day value (in settings) is changed globally, that wont reflect the already set cookies and user devices.
Example: If you set that to 10 days and there is a user which decide to use Remember Device functionality, when you change that value to 15 days, that wont increase the time for that user. Same applies for decreasing the value.
Encryption key for 2FA secrets
Authenticator secrets are stored encrypted (XChaCha20-Poly1305), so a copy of the database alone - a leaked backup, an SQL injection - does not let anyone generate your users' codes. That only holds if the key is kept somewhere other than the database. The plugin takes the key from the first of these that exists:
WPSEC_ENCRYPTION_KEY environment variable. Best choice when your host lets you set environment variables (Docker, most managed hosts, or fastcgi_param WPSEC_ENCRYPTION_KEY "..."; in nginx). It always takes priority.WPSEC_ENCRYPTION_KEY constant in wp-config.php: define( 'WPSEC_ENCRYPTION_KEY', '...' );openssl rand -hex 32. A shorter value is ignored and reported as an error; users then cannot set up an authenticator until it is fixed.
When none of these exists, the plugin creates a key itself - on activation, the first time the plugin settings page is opened, or the first time one is needed - and tries to add it to the top of wp-config.php, right after the opening <?php (after a declare() line, if the file starts with one):
// BEGIN Secured WP - encryption key for the stored 2FA secrets. Do not change or remove it; see the Secured WP readme.
defined( 'WPSEC_ENCRYPTION_KEY' ) || define( 'WPSEC_ENCRYPTION_KEY', '<64 hex characters>' );
// END Secured WP
The top of the file is used because every wp-config.php starts with <?php, while the "That's all, stop editing!" line differs between hosts or is missing. The plugin only writes when wp-config.php is writable and does not mention WPSEC_ENCRYPTION_KEY yet, never when the file declares a namespace, and only after PHP itself has parsed the result. It writes in place (owner, permissions and symlinks are kept) and puts the original back if the result does not read back exactly. To stop the plugin from ever editing wp-config.php, add add_filter( 'wpsec_write_wp_config', '__return_false' ); to a must-use plugin.
When wp-config.php cannot be written, the key is stored in the database, and the plugin settings page and Tools > Site Health say so. To move it out of the database, do one of these:
wp wpsec move-key), then make the file read-only again. The same key is written to the top of the file and deleted from the database, so nothing needs re-encrypting.wp wpsec move-key prints it too when it cannot write). Put it at the top of wp-config.php, right after <?php - anywhere before the line that loads wp-settings.php works - or, where the host manages wp-config.php, in the file the host tells you to use for your own settings.WPSEC_ENCRYPTION_KEY environment variable to the value between the quotes in that block.wp wpsec key-status).
If your deployments replace wp-config.php (git, CI, or a host that manages the file), the block the plugin wrote would be removed on the next deploy. Copy it into the wp-config.php you deploy, or use the environment variable. If the key goes missing while users still have authenticators encrypted with it, the plugin does not create a new key over it: the settings page, the Users screens and Site Health report how many authenticators cannot be read, until the key is restored or those authenticators are reset.
Changing or adding a key: every stored value records which key encrypted it. The plugin reads with any key it still has, and re-encrypts with the current one (the first in the list above) whenever a secret is read.
WPSEC_ENCRYPTION_KEY_PREVIOUS (environment variable or constant) and the new value into WPSEC_ENCRYPTION_KEY. The previous key is only ever used to read.wp wpsec reseal. Once it reports nothing unreadable, the old key can be removed: delete WPSEC_ENCRYPTION_KEY_PREVIOUS or the old constant, and wp wpsec reseal --drop-database-key deletes the database fallback key.
Never remove or change the only key that encrypted the stored secrets. Those users' authenticators cannot be read any more: they are told so at login, can use the emailed link or a passkey if enabled, and must have their authenticator reset. Salt changes do not affect the key.
Secrets stored by earlier versions (encrypted with a key derived from the WordPress salts) are read and re-encrypted with the new key automatically. If the salts were changed before the upgrade, those secrets cannot be read and must be reset.
Backup codes
Right after setting up the authenticator, each user gets ten backup codes (XXXXX-XXXXX), shown once, with Copy, Download and Print. Each code signs in once instead of an authenticator code - at login ("Lost your phone? Use a backup code instead") and when confirming identity for security changes. So a user who lost their phone can sign in, confirm their identity with another code, reset their authenticator on their profile and set it up on the new phone, without an administrator. Codes also work when the stored authenticator cannot be read because its encryption key was lost.
The profile has its own Backup codes section next to the authenticator: how many are left and when they were made, always visible, with "Make backup codes" (it asks the user to confirm their identity first when needed; a new set replaces the old one). Users who set up the authenticator before backup codes existed get a first set, once, at their next login, and every user with two-factor login sees an admin notice when they have no codes or 2 or fewer left - dismissible until that changes. Administrators see a user's count on their profile, never the codes, and cannot make them for someone else.
Codes are stored as password hashes, independent of the encryption key and the salts, and each can be used exactly once, even by simultaneous requests. Resetting the authenticator retires the codes; the next setup comes with a new set. For support, wp wpsec backup-codes <user> --generate prints a fresh set to hand over.
Signed-in sessions
The Secured WP panel on the profile lists every place the account is signed in right now - browser, IP address, when it signed in and when the session expires - marks "this browser", and logs sessions out one by one or all others at once. Administrators can do the same for users they may edit ("Log out everywhere"). Logging out single sessions needs WordPress's own session storage; if a plugin replaces it, only "log out all other sessions" is offered.
Resetting a user's authenticator
A reset removes the authenticator and its backup codes and forgets the user's remembered devices; they set up a new one (with new backup codes) at their next login. It is needed when a phone is lost, or when an authenticator cannot be read (its encryption key was lost). The Users screen shows "UNREADABLE - reset required" in the Secured WP Status column for those.
wp wpsec reset-authenticator <user>..., or wp wpsec reset-authenticator --unreadable for every authenticator that cannot be read.wp wpsec; wp help wpsec <command> shows the built-in help. They act on the whole installation - on multisite the key and the users' authenticators are network-wide, so --url makes no difference. Anyone who can run WP-CLI already controls the site, so the commands do not ask for identity confirmation the way the admin screens do.
wp wpsec key-status
Shows where the encryption key comes from (environment variable, wp-config.php constant, or database), how many keys are available for reading, how many authenticators are stored (and how many are still in the pre-2.5 format), and warns about a misconfigured key, a key kept in the database, or authenticators encrypted with a key that is no longer available. If the database still holds a copy of a key that is now also set in wp-config.php or the environment, the database copy is deleted and the command says so.
wp wpsec move-key
Moves the key stored in the database to the top of wp-config.php, between the "BEGIN Secured WP" and "END Secured WP" lines, then deletes the database copy. It is the same key, so nothing needs re-encrypting. wp-config.php must be writable while the command runs; it can be made read-only again afterwards. If the file cannot be written, the command prints the exact block to paste by hand. It does nothing when there is no database key, and refuses when a key is already set in the environment or wp-config.php (use reseal --drop-database-key then).
wp wpsec move-key
wp wpsec reseal [--drop-database-key]
Re-encrypts every stored authenticator with the current key and reports how many were re-encrypted, how many were already current, and which users' authenticators cannot be read. Use it after adding or changing a key, instead of waiting for each user to sign in.
--drop-database-key - afterwards, delete the key stored in the database. Refused while the database key is still the current one, and while any authenticator could not be re-encrypted. Authenticator setups started in the last 15 minutes may need to be restarted.wp wpsec reset-authenticator [<user>...] [--unreadable]
Removes users' authenticators and forgets their remembered devices; each of them sets up a new authenticator at their next login. Use it for a lost phone, or for authenticators that cannot be read because their key was lost.
<user> - one or more user IDs, logins or emails (a number is looked up as an ID first).--unreadable - every user whose stored authenticator cannot be read. This decrypts every stored authenticator, re-encrypting the ones on an old key as it goes.wp wpsec backup-codes <user>... [--generate]
Shows how many backup codes users have left. With --generate (one user), replaces that user's codes with a new set and prints it - a support path for someone locked out without their phone; the old codes stop working. The user must have an authenticator set up. Signed-in sessions have WP-CLI's own wp user session list / wp user session destroy.
wp wpsec backup-codes alice bob
wp wpsec backup-codes alice --generate
Every single module can be enabled/disabled from its settings tab.
Yes - go to users page - select users by pressing the check box next to the username, and from the drop down menu select the action you want to perform and click Apply.
wp wpsec backup-codes.
New: signed-in sessions on the profile - browser, IP, sign-in and expiry times, "this browser", log out one session or all others.
Security: passing a user to the plugin's User helpers no longer silently makes it the "current" user, and an empty or unknown user resolves to nobody rather than to the logged-in user. Stored settings are type-checked on every read. "Delete data on uninstall" now also removes passkey credentials, remembered devices and old attempt counters.
Removed: unused code and third-party libraries (Bacon QR Code, DASPRiD Enum, ParagonIE, PSR, Spomky, Symfony), the legacy users-list class, the unused JIT asset compilers, the unused password validator and the settings Export/Import tab that had no handler; the plugin no longer defines a global trigger_deprecation() function. Namespace WPSEC\Mosules\Views renamed to WPSEC\Controllers\Modules\Views.
Security: authenticator secrets are stored only after a code from them verifies; a secret shown during a login that was not finished is never used.
Security: an enrolled authenticator secret is no longer shown on the profile page, to the user or to administrators - reset and enroll again instead.
Security: Remember devices skips only the 2FA code; it no longer signs the browser in without a password.
Security: login attempts are locked per source address, with an account-wide backstop that remembered devices get past; the counter no longer re-locks right after a lock ends; administrator emails are sent once per lock period.
Security: the custom login slug no longer leaks through /wp-admin/options.php (CVE-2021-24917).
Security: with only Out of band e-mail enabled, users now get the emailed sign-in link as the second step. Before, the password alone signed them in.
Security: login, code and identity-confirmation limits are counted atomically and before checking, so simultaneous requests can no longer each get the full allowance (before, a burst of parallel code guesses was limited only by the number of PHP workers).
Security: an administrator's lock also stops application passwords.
Security: authenticator secrets are encrypted with a key kept outside the database (environment variable or wp-config.php, database only as a fallback) instead of one derived from the salts. Changing the salts no longer breaks every login; an unreadable secret is reported instead of crashing the login page. The key is written at the top of wp-config.php (not above a "stop editing" line, which some hosts do not have); a key that had to be stored in the database can be moved with a button or wp wpsec move-key, or copied by hand, after which the database copy is deleted. No new key is created over a lost one. WP-CLI commands: wp wpsec key-status, move-key, reseal, reset-authenticator.
Security: "Reset authenticator" on the Users screens (row and bulk action, network Users screen on multisite); the status column flags authenticators that cannot be read. Bulk actions ignore user IDs that are not real users (a 0 used to act on the administrator running it).
Fixed: the setup QR code did not scan - the format information was written to the wrong cells, and a link too long for the code was silently cut short. Codes now match a reference encoder bit for bit. The built-in encoder (no third-party library) now covers all QR versions 1-40 (up to 2331 bytes, previously 213), so long and non-Latin site names get a working code; dense codes get a "Show a larger QR code" link so a phone can scan them; the QR markup is 6-7 times smaller. If a QR code still cannot be drawn, the page says so and offers the "Open in authenticator app" button and the setup key instead of failing.
Authenticator setup: "Open in authenticator app" button (otpauth:// link) and a Copy button for the setup key; the code field supports one-time-code autofill; the authenticator app now shows the account name next to the site name.
Profile page: Secured WP's options are grouped in one branded panel (also in the shortcode and WooCommerce My Account).
Security: a WPSEC_ENCRYPTION_KEY line commented out in wp-config.php is no longer used; wp-config.php is not written when the disk is nearly full.
Fixed: a module setting stored as something other than true/false (a hand edit, an import) no longer causes a fatal error on every page.
2.4.0
Update to WP 7.1
2.3.2
Small update - added option to remove styles for gutenberg (core WordPress)
2.3.1
Small update - added option to remove styles for classical themes (core WordPress)
2.3.0
Maintenance update. Tested up to WP 6.9. Added jquery scripts removal logic.
2.2.4
blueprint live preview fixes.
2.2.3
Bug fixes related to login attempts.
2.2.2
Updated TOTP library.
2.2.1
PHP 8 fixes.
2.2.0
Updated libs and fixed deprecations.
2.1.1
Small bug fixes with redirection
2.1.0
Removed all jQuery dependency when custom page (or post) with shortcode is used for user's settings manipulation. Fixed lots of bugs
2.0.3