Category: Security

  • WordPress Spam Registrations: The Playbook, Step By Step

    WordPress Spam Registrations: The Playbook, Step By Step

    Open the Users screen and filter to Subscriber.

    If the list is full of accounts you do not recognise, with usernames that look machine-made, you are looking at the thing this post is about.

    The harder question is not how to delete them. It is which settings let them in, and whether the comment queues are filling up through the same open door.

    Short answer: WordPress spam registrations are new accounts created on a site by people you did not invite. Two settings decide how easy that is: whether anyone can register, and which role a new account gets. A read-only playbook can check both, count the comment queues and list accounts that are worth a look, without changing a user or a comment.

    What this post is, and is not: A method write-up of one read-only playbook, based on its two step files as shown in the Protuno dashboard and on its public page on 27 October 2026. It is not a result from a real run: no playbook was run for this post, and every number in the images and video is an example value. It does not say how spam accounts get past a registration form on any particular site, because that depends on the site.

    Two Doors Spam Walks Through

    A WordPress site has two common ways for a stranger to leave something behind. One is an account. The other is a comment.

    The account door is set in two places on the Settings, General screen. The WordPress documentation for that screen describes the first as “Anyone can register”, a checkbox for when “you want anyone to be able to register an account on your site”. The second is New User Default Role, which “allows you to select the default Role that is assigned to new users”. The valid choices it lists run from Subscriber up to Administrator.

    Those two settings work together. Open registration with a Subscriber default is a small risk. Open registration with a higher default role is a different kind of problem, because every stranger who signs up arrives with more power. The user roles post explains what each default role can do.

    The comment door is set on the Settings, Discussion screen. The documentation for that screen lists, among others, a checkbox that only lets logged-in, registered users comment, and a setting that closes comments on older posts after a number of days. A site where comments stay open everywhere and anyone can register has two open doors, and the queues behind them can grow without anyone noticing.

    An exposure audit reads both doors in one pass. It does not close either.

    WordPress spam registrations, illustration of two doors. The account door is Anyone can register, with the New User Default Role deciding what a new account can do. The comment door is comments left open, with the held, spam and trash queues growing behind it. Not a screenshot.

    What An Exposure Audit Looks At

    The playbook’s public page says it looks at three things:

    • Comment settings and queue sizes. Whether comments and pings are open by default, where they are still open, and how many comments sit in each queue.
    • Users by role and recent signups. How many accounts exist in each role, and how many registered in the last 30 days.
    • Accounts worth a look. Accounts with a throwaway email domain or a username that looks machine-made.

    It then gives an exposure rating and the next steps that apply. The next sections go through how.

    The Playbook, Step By Step

    The Comment & Signup Spam Exposure Audit playbook has two steps, in a fixed order. In the dashboard it carries a Beta label, sits in the Content group and only reads. The public page files it under Security, says it needs no connector, and says it runs when you start it with no schedule set. This post uses the public page’s category.

    A silent walk-through of the two steps. All scenes are illustrations with example values, not a real run.

    Step 1: Read Exposure Signals

    Step 1 is read-only. Every call it makes is a GET. It does four things:

    1. It reads the settings that open the doors: the default comment and ping status for new posts, whether anyone can register, the default role, whether comments are held for moderation, whether commenters must be registered, and whether comments close on old posts.
    2. It counts the comment queues: held for moderation, spam, trash and approved. It reads them 100 at a time, up to 20 pages, and reports “2000+” when a count hits that limit.
    3. It looks at where comments are still open on posts, pages and media, reading up to 10 pages of each.
    4. It counts users by role and how many registered in the last 30 days, using the 500 newest accounts.

    From those users it picks out accounts worth a look. That part gets its own section below.

    Step 2: Spam Exposure Report

    Step 2 makes no calls. It takes the numbers from step 1 and rates exposure as Low, Medium or High from fixed rules, then writes a report with these headings: Comments, Signups, Accounts worth a look, Not readable on this connection, Next steps, and Note.

    “Not readable on this connection” matters. If an option cannot be read, the report says so instead of guessing.

    How To Read A Rating

    RatingIt is given when any of these is true
    HighPending plus spam comments are 500 or more.
    High10 or more accounts are worth a look.
    HighComments are open on more than half of posts and 50 or more are pending.
    HighRegistration is open and the default role is above Subscriber (above Customer on a shop).
    MediumPending plus spam comments are 50 or more.
    MediumAt least one account is worth a look.
    MediumNew posts are open for comments by default.
    MediumRegistration is open.
    LowNone of the above.
    WordPress spam registrations, illustration of three rating cards. Low applies when none of the Medium or High rules applies. Medium is given for example at 50 or more pending and spam comments, or one account worth a look. High is given for example at 500 or more pending and spam comments, or a default role above Subscriber. Not a screenshot.

    The rules are plain counts, so you can check any rating against the numbers in the report. A Medium rating on a site that simply allows registration is not an alarm. A High rating because the default role is above Subscriber is worth fixing today.

    The report’s next steps depend on what it found. They can point to the Spam Comment Deleter playbook when pending plus spam is 50 or more, to the Disable Comments playbook, to a review of the listed accounts in the Users screen “before changing anything”, and to fixing the default role or unticking “Anyone can register”. If something could not be read, it adds a pointer to check Settings, General and Discussion by hand.

    What “Worth A Look” Means

    This is the part most likely to be misread, so the step file is strict about it.

    Only accounts with the Subscriber role, or Customer on a shop, are considered. An account is called worth a look for one of two reasons:

    • Its email is on a fixed list of 30 throwaway domains, such as mailinator.com.
    • Its username looks machine-made. Two patterns count: a username with no separator that ends in six or more digits, or one of ten or more characters that switches between letters and digits four or more times.

    A few rules sit around that:

    • The report shows only the username and the email domain, never a full email address.
    • It says “worth a look”. It never calls an account fake, a bot, a spam account or malicious.
    • It never recommends deleting, banning or removing a user.

    The caution is fair. A real person can have a username that ends in digits, and a real person can use a throwaway address for a free download. A flag is a prompt for you to open the account and decide, nothing more. The playbook only reads, so no account changes whichever way you decide.

    What The Settings And The Public Page Say

    The two sources agree on the substance. Both say the playbook only reads, has two steps and needs no connector, and the public page says no step is set to pause for your approval. The one difference found is the group label: the dashboard says Content, the public page says Security.

    The settings panels for both steps, viewed in the dashboard on 27 October, show Automatic backup None and Approval checkpoint Off. For a playbook that changes nothing, that fits: there is nothing to back up and nothing to approve.

    What The Playbook Does Not Do

    The step files and the public page set clear limits:

    • It does not stop anyone registering. There is no blocklist and no email check at signup.
    • It does not say how an account got in. It reads signals; it does not trace them.
    • Counts stop at 2,000 comments per queue and at the 500 newest users, and the report says when a count hit its limit.
    • It flags only Subscriber and Customer accounts. An account with a higher role is not flagged for its email or username.
    • It does not approve, trash or delete any comment, and it does not change any user.
    • “Worth a look” is not a verdict. A clean report is not proof that nothing is wrong.

    A plugin that blocks signups is a separate tool. A read-only audit tells you how exposed you are; it does not close anything.

    Two cases deserve their own posts. If spam accounts keep appearing after you close registration, the question changes from exposure to compromise, and WordPress Hacked: The First Hour, In Order covers what to do first. And if you want to know which accounts can be found from outside, the user enumeration post is about the routes that list usernames.

    A Careful Version You Can Do By Hand

    You can read the same signals yourself. It takes longer as the site count grows.

    1. Open Settings, General. Note whether “Anyone can register” is ticked and what New User Default Role says. If registration is open and the role is above Subscriber, fix that first.
    2. Open Settings, Discussion. Note whether commenters must be registered, and whether comments close on older posts.
    3. Open Comments and read the count beside Pending, Spam and Trash.
    4. Open Users and click Subscriber. Read the usernames and the email domains on that list. A page of near-identical names is a reason to look closer.
    5. Write down what you found and the date, so the next check has something to compare with.
    6. Only then decide what to change. Closing registration and trashing spam are separate decisions, and neither needs to happen the same day.

    On one site this is a short job. Across twenty sites it becomes a recurring chore, and the repeatable part is the reading. The judgement stays with you: which accounts to open, and whether a site needs open registration at all.

    The two-factor post is the next layer if a flagged account turns out to be a real one with a weak login. It separates turning two-factor on from enforcing it.

    Two Outside Views

    A hosting company’s video and a forum thread. Both are about neighbouring problems rather than signup spam itself, and neither describes the Protuno playbook. They are here for the habit they share with it: read what is there before you change anything.

    Ahosting INC’s “Could Not Create Directory in WordPress: Find the Folder First (2026)“, published 5 October 2026, is about a plugin install error, not about accounts. Its advice is to diagnose first: find the folder path, check disk space and inode limits, and only then look at folder modes.

    Check both your space and inode gauges in your control panel or you will end up chasing permission ghosts forever.

    Ahosting INC, Could Not Create Directory in WordPress: Find the Folder First (2026), auto-generated English captions, 5 October 2026

    It is a hosting company’s explainer that points to its own written guide, and the captions are automatic. The same video is a source in the file permissions post, which covers the error itself.

    In an r/Wordpress thread, a poster describes investigating a compromised site. They say an attacker used a valid administrator session, installed a file manager plugin and used it to upload executable PHP files. Their main lesson:

    A successful WordPress admin login should be treated as a full application compromise.

    u/ThaiHeartly, r/Wordpress, 11 August 2026

    The poster says how the administrator session was first obtained is unknown. This is one poster’s account that I could not verify, and some replies asked whether the write-up was machine-written, which the poster denied. My reading, not theirs, is that the playbook would not see a case like this. It flags only Subscriber and Customer accounts, and an account created with a higher role is outside what it flags. The user roles post is the place to look at those.

    Where Protuno Fits

    The Comment & Signup Spam Exposure Audit playbook is the reading part of this job. It reads the comment and signup settings, counts the queues and the recent signups, lists accounts worth a look, and writes a Low, Medium or High rating with the rule that decided it. It needs no connector, runs when you start it and has no schedule set. It changes nothing.

    Two other playbooks cover the writing side, and the report points to them when they apply. Spam Comment Deleter trashes comments confirmed as spam, and Disable Comments stops new comments. Protuno publishes its playbooks as written-out steps you can read first. The playbooks page lists them, and this one has its own page at Comment & Signup Spam Exposure Audit.

    The Point

    Open registration is a choice, and the default role decides how big a choice it is. The comment queues are a second door. Neither tells you anything until someone reads them.

    Read both doors, treat a flag as a question, and keep the decision about each account with a person. A rating on paper is a reading list, not a verdict.

    If someone registered on your busiest site tonight, would you know which role they got?

    Sources And Further Reading

  • WordPress File Permissions: Check The Owner First

    WordPress File Permissions: Check The Owner First

    A plugin install stops with “Installation failed: Could not create directory.” An upload in the media library says “Unable to create directory wp-content/uploads/2026/10. Is its parent directory writable by the server?” Or an update stops on a screen that says “To perform the requested action, WordPress needs to access your web server. Please enter your FTP or SSH credentials to proceed.”

    The advice for all three is usually a number: set the folder to 777. The WordPress handbook warns against it, and a hosting company’s video about the first error says you never need it. These messages are not asking for a bigger number. They say WordPress tried to write something and the server refused, and the refusal has more than one possible cause.

    Short answer: find out who owns the files and which account PHP writes as, then read the mode. WordPress itself decides whether it can write by comparing owners, not by reading digits. For servers where PHP runs as the file owner, the handbook’s baseline is 755 for directories and 644 for files, with wp-config.php at 440 or 400, and it treats 777 as a number to question. The number is the last thing to change.

    What this post is, and is not: a walk-through built from the WordPress handbook, the WordPress 7.1.3 source on GitHub, two videos and one forum thread. I did not test anything on a site, and the commands near the end are read-only ones I have not run. The right values depend on your host, so the post says where to measure instead of giving one number to copy. Latest checked: WordPress 7.1.3 (6 October 2026) is the current stable release and 7.2 Beta 1 came out on 20 October. Neither announcement mentions file permissions or the filesystem.

    Four cards in a row showing the WordPress write test: create a test file in wp-content, compare the owner of that file with the owner of a core file, write directly from PHP when the owners match, and ask for FTP or SSH details when they differ.
    Illustration of the check described in the WordPress 7.1.3 source. Example wording, not a screenshot.

    Three Digits, Three Audiences

    A mode is three digits: the owner, the group, and everyone else. Read is 4, write is 2 and execute is 1, and each digit is the sum. So 7 is read, write and execute, 5 is read and execute, and 4 is read only. That is the scheme the handbook’s file permissions page describes.

    ModeOwnerGroupEveryone else
    755read, write, executeread, executeread, execute
    644read, writereadread
    777read, write, executeread, write, executeread, write, execute

    “Everyone else” means every other account and process on that server. The handbook’s warning about 777 is about those accounts and processes, not about visitors reading files.

    How WordPress Decides It Can Write

    Before it writes during an install or update, WordPress runs a small test. In the 7.1.3 source it is the function get_filesystem_method() in wp-admin/includes/file.php. Unless the FS_METHOD constant is defined, it works like this:

    1. It tries to create an empty file named temp-write-test- plus a random string inside the wp-content directory.
    2. If that works, it compares the owner of the new file with the owner of a WordPress core file.
    3. If the two owners match, it writes directly from PHP.
    4. If they differ, or the test file could not be created at all, it falls back to SSH or FTP and shows the credentials screen quoted at the top.

    There is one more branch. A caller can allow “relaxed” ownership, and then a writable directory is enough to alter existing files, though that mode never creates new ones. The code comment says so.

    Two consequences follow from reading the code. I have not run either. First, a folder at 755 that is owned by the account PHP runs as passes the test, so no 777 is needed. Second, loosening a folder to 777 does not fix an owner mismatch. PHP can create the test file, but if PHP runs as a different account from the one that owns the core files, the new file has a different owner and WordPress still asks for FTP details. The same prompt also appears when the test file cannot be created at all, which is why it can mean two different problems.

    The handbook describes the working case for servers it calls “suexec”, where PHP runs as the owner of the files. There, it says, WordPress “will not ask for FTP credentials when upgrading or installing plugins”.

    FS_METHOD is the documented override. The handbook’s wp-config page says it forces the filesystem method and that you should generally change it only if you have update problems. It also says “direct” is chosen automatically when appropriate, and that forcing it is “fraught with opening up security issues on poorly configured hosts”. In the code, defining it skips the test above. It does not change who owns anything.

    What The Handbook Recommends

    The handbook’s file permissions page gives a scheme for servers where PHP runs as the file owner, and its hardening page repeats the key values.

    WhatHandbook valueWhere it says so
    Directories755 or 750File permissions page, the suexec scheme
    Files644 or 640Same scheme
    wp-config.php440 or 400Same scheme. The hardening page says only you and the web server should be able to read it, which “generally” means 400 or 440
    Upload directoriesNot 777: “No directories should ever be given 777, even upload directories”Same scheme
    wp-contentWritable by your user account and the web server processHardening page
    Plugin filesWritable only by your user accountHardening page

    The page also notes that wp-config.php is “initially created with 644 permissions, and it’s a hazard to leave it like that”.

    Three cautions on that table. First, the handbook says permissions differ from host to host, and that its guide covers general principles. Second, the page is not consistent about 777. Its general section says to start restrictive and raise permissions until it works, lists modes to try in order, and ends with “Only use 777 if necessary, and hopefully only for a temporary amount of time.” It also says “when you see 777 ask why it’s necessary”. Third, the hardening page says automatic updates run as the file owner and set files to 0644 and directories to 0755, so a mode you set by hand on core files is not guaranteed to survive. Check after an update, and read the auto updates post for what updates run on your site.

    777 In Plain Terms

    The handbook says 777 makes a file or directory readable, writable and executable by all users, groups and processes. Its worst case is that if an attacker can upload or modify a file to execute code, “they will have complete control over your blog, including having your database information and password.”

    The description of that same video, from Ahosting INC, is blunter. It says the usual advice is to make wp-content 777, and “You never need it.”

    The better fix is the one the handbook points to: any file that needs write access from WordPress should be owned, or group-owned, by the account WordPress runs as. That means changing the owner, then keeping the mode at 755 or 644. It does not mean raising the mode until the error goes away.

    Four Causes, One Message

    The same sentence can come from causes that have nothing to do with a mode.

    Four cards in a row naming four causes of the same WordPress could not create directory error: wrong owner, mode too strict, disk or inode limit, and security module.
    Illustration. Each card is a cause named in the handbook or the Ahosting video.
    • Wrong owner. The Ahosting video says that if a 755 mode fails to let you write to a directory, the folder likely belongs to a different user account.
    • Mode too strict. The Ahosting video says a security plugin may have hardened folders to 555. The handbook’s .htaccess example says a mode set too low stops the server reading the file, which causes an error.
    • Disk or inode limit. The Ahosting video says WordPress creates every empty folder before it writes a file, that each folder needs an inode, and that an account near its inode limit can fail on folders while the storage gauge still shows room. It also says the server needs slightly more than twice the unpacked plugin size to hold the zip and the extracted copy.
    • A security module. The handbook says SELinux denials are often hard to tell from regular permission errors. It is typically installed on Red Hat family systems, and it can block writing the .htaccess file.

    I confirmed that the sentence “Could not create directory.” appears in the 7.1.3 file wp-admin/includes/file.php, and that the upload message above appears in wp-includes/functions.php. I did not count the places the Ahosting video says the sentence comes from.

    Find The Cause In Order

    The Ahosting video’s order is a good one: find the path, check space and inodes, then look at modes and ownership, and only then change anything. Adapted, with two extra steps:

    1. Read the full message. The Ahosting video says the Install Now button drops the folder path from its output, and shows two ways to retrieve it, which its written guide also covers. The path tells you which folder failed.
    2. Ask WordPress what it can write. Go to Tools, then Site Health, then the Info tab, and open Filesystem Permissions. In 7.1.3 it lists the main WordPress directory, wp-content, uploads, plugins, themes and fonts, plus the must-use plugins directory if it exists, each as Writable or Not writable. It asks PHP, so it reports what the web process can do, not what your own login can do.
    3. Check space and inodes. Look at both gauges in your hosting panel, or run the commands below on a server where you have a shell.
    4. Compare owner and mode. List the owner, group and mode of wp-config.php, wp-content, uploads and plugins, and compare the owner with the account PHP runs as. Your host can tell you which account that is. A command-line php run reports the command-line user, which may not be the one that serves pages.
    5. Check for a security module. On Red Hat family systems, ask whether SELinux is enforcing.
    df -h
    df -i
    stat -c '%U %G %a %n' wp-config.php wp-content wp-content/uploads wp-content/plugins
    getenforce

    These are read-only commands, and I have not run them on a site. stat -c is the GNU form and differs on macOS. getenforce exists only where SELinux is installed. On shared hosting without a shell, the same information is usually in the file manager and the hosting panel.

    Write down the result before you change anything. If the owner is the account PHP runs as and the modes match the handbook’s table, the mode was never the problem, and the next suspect is space, inodes or a security module.

    The Outside View Is A Different Check

    Nothing about a mode is visible over HTTP. An outside check can only show a file being served that should not be, and two earlier posts already cover that: the exposed twin of wp-config.php and the debug log that is readable from outside. Run those alongside this one. This post is the inside view.

    For a wider look at a whole install, the hardening measurements test advice on live sites before repeating it, which is the right habit for any permission change too.

    What Correct Permissions Do Not Solve

    Permissions say who on the server may touch a file. They are a small part of the picture.

    • A write made through WordPress. WordPress writes as whichever account PHP runs as. A plugin weakness, or a stolen administrator login, can make it write something unwanted even when the modes are correct.
    • An administrator. Anyone who can install plugins or edit files from the dashboard works around the filesystem question. Who really has the keys is the check for that.
    • File contents. A mode says nothing about whether a file changed. If something looks wrong, a malware scan and the first hour after a hack are the next steps.
    • A tightened mode that gets reset. The handbook says automatic updates set files to 0644 and directories to 0755.
    • A full account. As above, the error can come from space or inodes, and chasing a mode will not fix it.

    A permission pass belongs inside a bigger one. The security audit walk-through puts the order of work in one place.

    Two Outside Views

    Both picks below are about spam accounts, which is a neighbour of this topic rather than the same one. They are here for the point in the previous section: a correct mode does not say anything about who is allowed to create an account or what a form lets through.

    A video. WP Ultimate Security’s “Stop Fake and Spam Email Signups in WordPress“, published 3 August 2026, walks through two features of its own plugin, an email blocklist and email verification. It blocks addresses and whole domains, and it checks sign-ups, comments and WooCommerce checkout for badly formed, disposable or no-MX-record addresses. It tests each one on screen.

    It’s a simple way to keep spam accounts out and block problem users without having to delete anyone’s account manually.

    WP Ultimate Security, Stop Fake and Spam Email Signups in WordPress, auto-generated English captions, 3 August 2026

    It is a vendor’s tutorial for its own plugin, and the description says the features need both the free plugin and its Pro version. I did not test the plugin, and the captions are automatic. Nothing in the video touches file modes.

    A thread. In an r/Wordpress thread, a poster running a course site on Tutor LMS, with registration set to need manual administrator approval, describes accounts with crypto, casino and pharma names appearing as approved with no approval step and no notification email.

    Lately though, my site has been flooded with user registrations that are somehow automatically approved, no admin input required.

    u/realcryptogd, r/Wordpress, 18 September 2026

    The replies disagree. One says this is a backdoor symptom and suggests checking for hidden administrators, webshells in uploads and files in mu-plugins. Another asks why a backdoor would need registration open, and suspects a second registration endpoint that skips the approval step. The poster tried hiding the default login page and says it did not help, and the thread does not report a cause. My reading, not theirs, is that this is the kind of problem where a folder’s mode cannot tell you anything, because the accounts are created by the application, not by a file write. It is one poster’s account that I could not verify, and the replies are guesses.

    The Point

    Pick one site you manage. Without changing any mode, can you say which account owns wp-content and which account PHP writes as?

    If not, the number on the folder is not the thing to look at yet. The question is what WordPress sees when it tests a write, and what you would do if the answer were different on the next site.

    Sources And Further Reading

  • WordPress Application Passwords: The Playbook, Step By Step

    WordPress Application Passwords: The Playbook, Step By Step

    Open any user’s profile in the dashboard and scroll past the password box.

    Below it sits a section called Application Passwords, with a name field, a button that says Add New Application Password, and a list of the keys that already exist.

    Most people never scroll that far. The keys in that list can outlive the password you changed last week.

    Short answer: WordPress application passwords are separate, revocable keys that apps and scripts use to sign in without the account’s normal password. Each one stays valid until someone revokes it, so a site can carry old keys nobody remembers. The New Admin Watch playbook reads every administrator and every application password, flags the recent and never-used ones, and, if you paste in a baseline, shows what changed since the last run. It only reads. It never creates, changes or revokes anything.

    What this post is, and is not: A method write-up of one read-only playbook, based on its two step files as shown in the Protuno dashboard and on its public page on 22 October 2026. It is not a result from a real run: no playbook was run for this post, and every username, date and id in it is an illustration with example values.

    What WordPress Application Passwords Are

    Application passwords arrived in WordPress 5.6. The core team’s integration guide describes them as a way to make authenticated requests to WordPress APIs without using the account’s normal password. Cookie sign-in stays as it was. An application password cannot be used on the login page, because it is meant for programs, not people.

    The same guide says how one is stored:

    • It is 24 characters of upper-case letters, lower-case letters and numbers.
    • It is kept in the user’s meta data, with the name given to the application, the time it was created, and when and from which IP address it was last used.
    • Each one also gets its own uuid, so it can be referred to without using its name.

    You manage them from the Edit User page, where you can create new ones and view or revoke the ones that exist. The Last Used column is accurate to within 24 hours, because WordPress only writes the date once a day. The guide says that makes it useful for finding passwords that are no longer in use, so they can be revoked.

    By default the feature is available on sites served over HTTPS and on local environments, and the developer reference says a filter can change that. The REST API handbook adds that the credentials go to REST requests over HTTPS using Basic Auth.

    Latest changes checked on 22 October 2026: WordPress 7.1.3 (6 October) is the current stable release, and 7.2 Beta 1 came out on 20 October. Neither announcement mentions application passwords or administrator accounts. So everything above comes from the 5.6 guide, the developer reference and the REST API handbook, not from release notes.

    Why A Key Outlives A Password

    A password reset feels like a clean slate. An application password does not care.

    A tutorial from SmartWP, covered below, makes the point in plain terms. An application password is a separate credential that you give to one app. You can issue several, and you can revoke one without touching the others. In his demo, changing the account’s normal password does not interrupt the app that holds a key.

    That is exactly why they are useful, and exactly why they get forgotten. The key sits in a list, nobody opens that list, and the app that used it may be long gone.

    A key also carries the powers of the account it belongs to. A post on WordPress MCP write access makes the point for AI connections: an application password issued to an administrator carries administrator capabilities.

    Two Lists To Read Together

    The first list is who can act as an administrator. The second is which keys exist for each user, and when each was last used.

    WordPress application passwords, illustration with two panels. The left panel is titled Administrators and shows three example accounts with a registered date. The right panel is titled Application passwords and shows three example keys, each with a short id, a created date and a last used date, one marked Never used. Example values, not a screenshot.
    Two lists side by side. Example values, not a real run.

    Neither list answers the real question on its own. “Who can get in?” needs both. A post on user roles covers listing every account and the three accounts to question first. This playbook adds the second list and puts both into one report.

    The Playbook, Step By Step

    The New Admin Watch playbook has two steps, in a fixed order. In the dashboard it carries a Beta label and only reads. The public page files it under Security and says it needs no connector. It runs when you start it, and no schedule is set.

    No step pauses for approval. I opened the settings panel for both steps, and both show Automatic backup None and Approval checkpoint Off, which fits steps that only read.

    A 33-second silent walk-through of the two steps. All scenes are illustrations with example values, not a real run.

    Step 1: Snapshot Admins And Credentials

    Step 1 makes one call to a Protuno read tool. The tool reads the site itself and applies every check in code. The step does not compare, count, judge or sort anything on its own. It copies what the tool returns into one fixed block for step 2.

    The step file says the tool applies eight checks, and names what it covers:

    • Which application password is this connection’s own, so it is not listed as a stranger.
    • Changes since the baseline, if one was pasted.
    • Recent accounts and recent passwords.
    • The admin username.
    • Passwords that have never been used.
    • Extra capabilities on a user.
    • Admin-level powers held without the administrator role.
    • Administrators through a second role.

    If the administrator list cannot be read, the step stops with “Could not read the administrator list.” and fails. In every other case it ends as a pass.

    The baseline is optional. It is three lines pasted into the playbook’s Instructions: a line starting BASELINE, then admins: and app-ids:.

    Step 2: Admin Watch Report

    Step 2 makes no calls and judges nothing. It lays out the block from step 1 in a fixed layout. A header line gives the number of administrators, the number of application passwords and whether a baseline was given, followed by the connection and the users by role. Then come six sections, always in this order:

    1. Checks.
    2. Changes since the baseline, as a table.
    3. Things to confirm, as a table with finding, user, detail, severity and what to do.
    4. Administrators, as a table with id, username, registered date, email domain and application passwords.
    5. Not checked.
    6. Baseline, a new set of three lines to copy.

    The findings are exactly the lines the tool returned. Nothing else counts as a finding, and the step may not add, drop, reword or re-rate a row. Two privacy rules apply. An email address is shown as its domain only. An application password is shown by the first 8 characters of its id, never by its name, because two passwords can share a name.

    WordPress application passwords playbook, illustration of two steps. Step 1, Snapshot Admins And Credentials, reads the administrators and their application passwords and makes one call. Step 2, Admin Watch Report, makes no calls and lays out the checks, the changes and the things to confirm. Both steps show Automatic backup None and Approval checkpoint Off. Example layout, not a real run.
    The two steps and what each hands to the next. Settings as shown in the dashboard, not a real run.

    How A Baseline Turns A List Into A Change

    Without a baseline, the report says “No baseline given: date-based checks only.” An administrator or an application password created in the last 30 days is flagged as recent. That is a useful nudge, but it cannot tell you what changed.

    With a baseline, the next run can say what is different. The dashboard card says the next run also shows new administrators, raised roles and new application passwords. If there is a baseline and nothing moved, the report says “No changes since the baseline.”

    The report ends with a new baseline to paste back in next time. To use it, open the playbook page, find “Runs on these sites”, open Instructions for the site, paste the three lines, and save.

    That is the whole habit: read, paste, compare later. A list you read once tells you what exists today. A list you compare tells you what moved.

    Reading A Finding

    The report is strict about words. It must never say the site “is secure”, “is safe”, “was hacked” or “is compromised”. A finding is something to confirm with the site owner, not an accusation.

    That matters in practice. A new administrator may be a new hire. A never-used key may be a connection someone set up last week and has not tested yet. The row tells you where to look and who to ask. It does not tell you what happened.

    What The Report Does Not Cover

    The step files list what stays out of reach:

    • Users hidden from the REST API by a plugin or malware. The list comes from the REST API, so it can never be proven complete.
    • Changes made directly in the database.
    • Powers a custom role grants by itself. Only powers given directly to a user are read.
    • Login history and failed logins.

    It also never reads a password, and it creates, changes or revokes nothing. If you find a key you do not recognise, the revoking is your decision, made on the Edit User page.

    If the REST user list is part of the question for you, a post on WordPress user enumeration looks at how that user list is exposed from outside.

    A Careful Version You Can Do By Hand

    You can do a rough version of this without any playbook. It follows the same order.

    1. Open Users, filter to the Administrator role, and write down every username. Mark any you cannot place.
    2. Open each administrator’s Edit User page and scroll to Application Passwords.
    3. For every key, write down its name, when it was created and its Last Used value.
    4. Mark keys that were never used, and keys that have not been used for a long time. Remember Last Used is accurate to within 24 hours.
    5. For any key you do not recognise, find out who owns it before you revoke it. Revoking cuts off whatever app uses it, and in the SmartWP demo that is the point.
    6. Save your notes as a plain text file with each username, key name and created date. That file is your baseline.
    7. Repeat monthly and compare the two files.

    Two keys can share a name, so a note by name alone can mislead. That is why the playbook identifies each key by the first 8 characters of its id.

    Application passwords are one of the sign-in routes that do not use the login form. A post on two-factor enforcement lists those routes, and a post on the first hour after a hack includes application passwords in the WordPress layer of access to review.

    Two Outside Views

    A tutorial and a forum thread. Neither is an endorsement, and neither describes the Protuno playbook.

    In a November 2024 video, SmartWP explains how to set up and use application passwords safely. He says an application password is like a normal WordPress password but meant for one app, and that sharing it is safer than sharing your real password. He says you can make several and revoke access to a specific application. In the demo he creates one named for Zapier, says it can be pasted into Zapier, and notes that resetting his normal password would not interrupt it. When he is done with Zapier he revokes the key so Zapier can no longer edit the site. The video is older than the releases above, and near the end he mentions a blogging platform he built that supports the feature.

    On Reddit, an r/Wordpress thread titled “How many people can log into your sites as Administrator, and do they open links?” starts with a site manager asking a simple question: “How many Administrator accounts are on the sites you look after?” This post does not rely on the opening post’s security claims. It uses the question and one reply, which describes the baseline habit in a few words:

    “We have a single administrator account on all the sites we manage, our own. There is a specific list of capabilities we have never given out and our nightly sync will alert us if this somehow changes in a site.”

    Reply in the thread, r/Wordpress, 3 October 2026

    Another reply in the same thread gives a different answer: “2-3 admin acccounts is the normal number but I only manage a couple dozen WP installations.” A count on its own does not settle much. What matters is whether you can see a change when it happens.

    Where Protuno Fits

    The New Admin Watch playbook is the repeatable, read-only part of this job. It lists every administrator and their application passwords, flags recent accounts, the default admin username, new or unused keys and extra capabilities, and writes a report with a baseline you can paste back in. It needs no connector, runs when you start it, and has no schedule set.

    It sits beside the other security posts rather than replacing them. The Security Audit playbook has its own post. The playbooks page lists the playbooks as Protuno publishes them, and this one has its own page at New Admin Watch.

    The Point

    A password is something you change. An application password is something you have to find.

    Read both lists together, keep a baseline, and treat every unfamiliar row as a question for the site owner. A report that revokes nothing is the safest way to see how many keys your sites are holding.

    Which keys on the sites you manage could you name today without looking?

    Sources And Further Reading

  • WordPress Two-Factor Authentication: What It Enforces

    WordPress Two-Factor Authentication: What It Enforces

    “Two-factor authentication: enabled.”

    That is the line a security report gives you, and it is the line that reassures a client. It describes one screen, the WordPress login form, where a code prompt appears after the password.

    A WordPress site can be signed in to in more than one way. A script can present a username and an application password to the REST API. An XML-RPC call can send credentials in the request body. A browser can already hold a session cookie from before the setting changed. Whether each of those passes through the code prompt depends on the plugin and its version.

    Short answer: WordPress two factor authentication comes from plugins, and what each one enforces depends on which sign-in routes it covers and whether the second factor is required or optional per user. The prompt after the password at wp-login.php is the part everyone checks. Whether the same plugin also covers XML-RPC, the REST API, application passwords and sessions that already exist is the part you confirm on your own site, with a request, not a feature list.

    What this post is, and is not: this is a way to find out what 2FA enforces on a site you are allowed to test. It is not a plugin ranking, and it does not say what any plugin does beyond what its own wordpress.org page says, quoted with the date it was read. Plugin behaviour changes between versions, so the only answer you can rely on is the one you measure.

    Four WordPress sign-in routes, wp-login.php, XML-RPC, REST API with an application password and an existing session cookie, each with a line to a WordPress site box. Only the wp-login.php line has a code prompt marked on it. The other three are marked to test.
    Illustration, not a screenshot. The code prompt is usually drawn on one route. Sign-in is possible on four.

    What The Official Plugin Says About Itself

    On the wordpress.org plugin directory, the community-maintained Two Factor plugin is one widely installed option. Read on 19 October 2026, its page showed version 0.17.0, 100,000+ active installations and “tested up to” WordPress 7.1.3, the current release (6 October 2026, seven security fixes, none of them described as a sign-in or two-factor change).

    The page is direct about what it does and does not enforce. In its own words and settings:

    • Setup is per user. Each user configures their own methods from their profile. An administrator can also configure another user by editing that profile.
    • Methods listed: an authenticator app (TOTP), email codes and backup codes. A dummy method exists for testing and needs WP_DEBUG.
    • No built-in enforcement. The FAQ asks whether you can require 2FA for all users or specific roles. The answer is not through the plugin’s interface, there are no built-in enforcement settings, and native enforcement is a tracked feature request (GitHub issue #255). On multisite, there are no network-wide settings either.
    • A locked-out user is a profile edit away. An administrator can untick every method in that user’s profile, and the user then signs in with the password alone.
    • Passkeys and hardware keys come from a separate plugin, Two-Factor Provider: WebAuthn, not from the Two Factor plugin itself.
    • API sign-in, by version. The changelog on the same page says version 0.14.0 (3 July 2025) turned on application passwords for REST API and XML-RPC authentication by default, while XML-RPC login stays disabled for regular user passwords. Version 0.17.0 (25 September 2026) lists a security fix to ensure that regular passwords cannot bypass the two-factor requirement for REST API and XML-RPC requests.
    • A filter for API sign-in. The page lists two_factor_user_api_login_enable, which it describes as restricting authentication for the REST API and XML-RPC to application passwords only.

    Read that as a list of questions for your own install, not a verdict. “Enabled” on this plugin means the options are available. Who has set one up, whether it is required and which routes it covers are separate facts. This is one plugin’s documentation on one date, and other plugins make other choices.

    What Each Method Proves

    Before asking where a factor is enforced, be precise about what each method proves. They are not interchangeable.

    MethodWhat it provesKnown weakness
    Authenticator app (TOTP)Possession of a device holding a shared secretThe secret can be phished at set-up, and the code can be relayed
    Email codeControl of the mailboxOnly as safe as the mailbox, and only works while the site can send email
    SMS codeControl of a phone numberSIM swap, interception, recycled numbers
    Push approvalPossession of an enrolled deviceApproval fatigue: people tap yes to make it stop
    Hardware key or passkeyPossession of a key bound to the site’s domainLost keys, and a weak recovery path
    Backup codesPossession of a stored listA complete bypass of the main method, by design

    The domain binding in the fifth row is the real difference, not the hardware. A key will not sign for a look-alike domain, so a phished login cannot be completed, while any code you type can be relayed by someone in the middle. And if backup codes sit in a shared password note, the enforced method is effectively the note.

    Enforcement Is A Separate Question From Method

    A strong method says nothing about who has to use it. Four questions decide whether 2FA is a control or a suggestion:

    1. Required, or optional per user? If it is opt-in, coverage is whatever share of people opted in.
    2. Can an administrator skip it? Administrators are the accounts worth protecting. If the highest role can remove its own requirement, or anyone else’s, that is the largest hole in the model.
    3. What happens to an unenrolled user? Blocked at sign-in until they enrol, or let through with a reminder. Those two answers describe different sites.
    4. What happens to sessions that already exist? If current cookies stay valid, someone already holding a session is unaffected by the change you just made.

    The fourth question turns a tidy change into a false sense of security. “2FA is now on” and “every live session is now backed by 2FA” are different statements, and the second usually needs sessions to be ended on purpose.

    The Routes That Do Not Use The Login Form

    This is the gap in the opening, and the reason for the whole exercise.

    RouteHow authentication happensWhat to check
    wp-login.phpInteractive form, username and passwordAlmost certainly covered. Confirm anyway
    XML-RPCCredentials in the request body, no formWhether the endpoint answers at all, and whether it accepts a password without a code
    REST API, cookie sessionExisting browser session plus a nonceIt inherits whatever the session was granted when it began
    REST API, application passwordUsername plus a generated password over basic authBuilt for sign-in without a form. That is its purpose
    WP-CLIA system process, not an HTTP sign-inNo second factor is involved, by design
    Host or panel single sign-onThe hosting panel drops you into the adminCheck whether it passes through the same second-factor step

    Two rows need more than a table cell.

    XML-RPC takes credentials directly, which is why it has long been a target for credential stuffing: one request can try many pairs without touching the login form. Whether a given 2FA plugin blocks it, ignores it or turns it off is something to check, not assume. Whether to turn the endpoint off at all depends on what still uses it, and that argument is in WordPress XMLRPC In 2026: Block It Or Leave It Alone.

    Application passwords are the harder case, because they are not a flaw. Core introduced them in WordPress 5.6 as a way to make authenticated requests without a form. The core integration guide says the credentials go to the REST API over HTTPS with Basic Auth, and that for the legacy XML-RPC API you use the application password in place of the account’s real password. A second factor cannot sensibly apply to a background script, so the useful questions are different: who can create them, how many exist right now, what is each one for, and does any belong to someone who has left?

    Attackers also need a valid username before a password is worth guessing, and WordPress is still generous with those. That half of the problem is in WordPress User Enumeration: Three Routes, One Fix Each, and it is why 2FA reduces risk rather than removing it.

    A Test You Can Run This Afternoon

    Run this on a staging copy, or a site you are explicitly allowed to test. Record what happened at each step, not the impression you came away with.

    1. Turn on 2FA with your chosen method. Enrol one administrator and leave one editor unenrolled.
    2. Sign out completely. Sign in as the administrator. Note whether the code prompt appears.
    3. Sign in as the unenrolled editor. Note whether you are blocked, prompted or let straight in.
    4. From the administrator’s profile, try to remove the requirement for yourself, then for another user. Note what the screen allows.
    5. Create an application password and use it for an authenticated REST request. Note the status and the body:
    curl --user "USERNAME:APP-PASSWORD" \
      "https://example.com/wp-json/wp/v2/users/me?context=edit"
    1. Make an XML-RPC call with the same credentials. Note the status and the body:
    curl -s -d '<?xml version="1.0"?><methodCall><methodName>wp.getProfile</methodName><params><param><value>1</value></param><param><value>USERNAME</value></param><param><value>APP-PASSWORD</value></param></params></methodCall>' https://example.com/xmlrpc.php
    1. Repeat steps 5 and 6 with the account’s regular password in place of the application password. Note the status and the body. A plugin that covers these routes should refuse it, but the result you record is the one that counts.
    2. Open a session in a second browser first, then change the enforcement setting and reload that session. Note whether it survives.
    3. Use one backup code. Confirm it works once and not twice.
    4. List every route that let you in without a second factor.

    Step ten is the output. The rest is instrumentation.

    A blank record sheet with five rows: wp-login.php for an enrolled administrator, wp-login.php for an unenrolled editor, REST with an application password, XML-RPC and a session open before the change. One column for whether a code was asked has empty Yes and No boxes, and another lists the evidence to keep.
    Illustration of a blank record sheet. Nothing on it is a measured result.

    A short silent video walks through the same ideas.

    Silent video, illustration, not a real run.

    The output of that run is a short table you can show a client, one line per route, with the evidence next to it. Compare that with “Two-factor authentication: enabled”. One is a finding. The other is a checkbox. The same difference runs through WordPress Hardening Checklist, Ranked By What It Changes, which ranks hardening steps by what they actually change.

    Two Outside Views

    A video from someone who tried a plugin. WPTuts walks through FluentAuth 3.0, a free plugin from the WPManageNinja team, from its set-up wizard to its login-protection options. The video description lists what it covers:

    “two-factor authentication, email verification codes, failed login lockouts, XML-RPC blocking, username protection”

    WPTuts, 100% FREE WordPress 2FA, Passkeys, Magic Login & More Plugin, video description, 18 September 2026

    It is a review of one plugin, so treat it as one person’s setup, not a comparison. It does show that XML-RPC blocking is listed as its own feature, apart from the two-factor codes.

    A thread from people living with it. In an r/Wordpress thread titled “I need to disable API logins. How do I do this and how can I verify that the blocking is working?”, the poster is seeing many failed logins even though /wp-admin/ is limited by IP. One reply explains why a login limiter might not see them:

    “limit login attempts won’t catch REST failures because they’re not going through wp-login.php”

    A reply in the same thread, r/Wordpress, 6 August 2026

    Another reply suggests checking the access logs to see whether attempts are hitting the login page, the XML-RPC file or the REST path, because those are three different paths. That is the same instinct as the test above: stop treating “login” as one door.

    Where 2FA Sits Among The Other Controls

    2FA defends one thing well: an attacker who has a valid password and nothing else. That is a large and common case, so the control earns its place.

    It does nothing about a stolen session cookie, because the factor was satisfied when that session began. The flags that limit how a cookie can be read are in WordPress Cookie Security: Secure, HttpOnly And SameSite. It also does nothing about a vulnerable plugin that allows privilege escalation without signing in, or when an attacker is using a browser that is already signed in.

    So it belongs inside a wider pass, not on its own. The surrounding sequence is in WordPress Security Audit: The Playbook, Step By Step, and the question of which accounts exist and what each can do is in WordPress User Roles: Who Really Has The Keys. If an account is already compromised, WordPress Hacked: The First Hour, In Order covers what to do next.

    What This Does Not Solve

    Turning on 2FA and testing it leaves these untouched:

    • Session theft. A valid cookie skips the prompt entirely. Session lifetime and the ability to end sessions matter more here than the factor does.
    • Real-time phishing. Any code you type can be relayed within its validity window. Only domain-bound keys resist that.
    • Recovery. The reset path, the support process and the backup codes are now the weakest way into the account, and attackers go there.
    • Non-interactive credentials. Application passwords, integration keys and host-level access sit outside the model by design.
    • The one unenrolled account. Coverage is a percentage, and the percentage is the control.
    • Whether the factor still works. An email-code method stops working the day the site stops sending email, and the failure can look like a login problem rather than a security one.

    That last point is quieter than it sounds. An email-code method depends on everything in WordPress Email Deliverability: Alignment, Not SPF, and a site that cannot deliver a code can end up with the requirement switched off in a hurry.

    The Point

    Comparing 2FA plugins by feature list compares marketing. Comparing them by sign-in route compares security.

    The test is dull and takes an afternoon: list every way a credential can be presented to the site, try each one with 2FA turned on, and write down which ones asked for a factor. On most sites, that table has at least one row that surprises somebody.

    So for the sites you look after: you know 2FA is enabled. Do you know, with evidence, every route that still signs in without it?

    Sources And Further Reading

  • WordPress User Roles: Who Really Has The Keys

    WordPress User Roles: Who Really Has The Keys

    Every WordPress site has an account nobody remembers creating.

    It might belong to a freelancer from two years ago, a plugin vendor’s support login, or a colleague who has since left. It still works. It is still an Administrator.

    The password is only half the problem. The bigger question is how many people can change your site today, and whether you could name every one of them.

    Short answer: To audit WordPress user roles, open the Users screen, filter by role, and count the Administrators. Each one can install plugins, edit code and create more users. Any Administrator with no named owner and no clear reason to exist is the first account to question. Then check whether anyone has a role above their job, and whether any login is shared.

    What this post is, and is not: A read-only audit of WordPress user roles, for people who look after several WordPress sites. The role descriptions follow how WordPress core documents its default roles. Nothing here is a result from a real client site, and it does not replace a full security audit.

    An Administrator is not a job title. It is a list of everything someone can break.

    Illustration of the five default WordPress user roles as a ladder, from Subscriber at the bottom to Administrator at the top
    The higher the role, the more a single stolen password can change.

    WordPress User Roles: What Each Default Role Can Do

    A single WordPress site ships with five roles. They are ordered by how much damage one stolen login can do.

    RoleWhat it can doWhat it should be used for
    AdministratorEverything on the site: install and delete plugins and themes, add and remove users, change any setting, and edit theme and plugin files unless file editing is switched offThe few people who run the site
    EditorPublish and edit all posts and pages, including other people’s, and moderate comments. No plugin, theme or user controlContent managers
    AuthorPublish and edit their own posts, and upload filesRegular writers
    ContributorWrite and edit their own posts, but not publish them or upload filesGuest writers
    SubscriberRead, and edit their own profileMembers, customers, commenters

    For a short overview of the defaults, there is a recent video, WordPress User Roles Explained (2026), from VIS Mountain Marketing & Advertising. Its description says it is narrated with an AI voice and shows simplified diagrams, not live screenshots, so use it as a refresher and check the official documentation for detail.

    What each WordPress role can really do, which one to give each kind of person, and how to change someone’s role in under a minute.

    VIS Mountain Marketing & Advertising, “WordPress User Roles Explained (2026): Which Role to Give Each Person + How to Change It” on YouTube, 6 Oct 2026

    Two things change this picture.

    First, plugins add roles and change what the default ones can do. A store plugin adds a Shop Manager. A membership plugin adds its own. These roles can carry real power, and they rarely stand out in a quick look at the Users screen.

    Second, a multisite network has a Super Administrator above every site. That one account controls the whole network, not a single site.

    Of all the WordPress user roles, one matters most for an audit. Administrator decides who can change everything else.

    Listing Every Account From Outside And Inside

    You can see your accounts and their WordPress user roles from two places, and the two lists do not match.

    From inside, the Users screen or the command line shows every account, its role and when it registered. From outside, you only see what the site chooses to reveal. By default, a WordPress site can reveal usernames through public routes. We covered all three of them, and how to close them, in WordPress user enumeration is still on by default, so this post does not repeat that.

    What matters here is the gap between the two lists.

    ViewWhat it tells youWhat it cannot prove alone
    Inside, Users screenEvery account and its roleWhether anyone still uses it
    Inside, command lineThe same list, in a form you can save and compare next monthWho the person behind a login really is
    Outside, public routesWhich usernames a stranger can findWhich of them are Administrators
    Illustration comparing the account list seen from inside WordPress with the shorter list visible from outside, with the gap highlighted
    An account that only shows up on one side is a question worth asking.

    A small audit you can run today:

    • Count the accounts in each role on the Users screen.
    • Save the Administrator list with name, email and registered date.
    • Write an owner and a reason beside every Administrator.
    • Mark every account you cannot explain.

    With command-line access, these two commands cover the first steps:

    wp role list
    wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

    The registered date is the cheapest clue you have. An old account with no known owner is the first one to question.

    One more caution. Code that runs inside WordPress can change what WordPress shows you. The r/Wordpress write-up linked below describes malware that kept a list of administrator accounts to hide, so a Users screen can look clean when it is not. If you suspect a problem, ask the database directly. This query lists every account with the Administrator role, and you should change each wp_ to your own table prefix:

    wp db query "SELECT u.ID, u.user_login, u.user_email, u.user_registered FROM wp_users u JOIN wp_usermeta m ON m.user_id = u.ID WHERE m.meta_key = 'wp_capabilities' AND m.meta_value LIKE '%administrator%'"

    If that list is longer than the Users screen, treat it as an incident and not a tidy-up.

    A 34-second walk-through of the audit. All scenes are illustrations with example accounts.

    The Three Accounts To Question First

    An audit of WordPress user roles does not need to be long. Three kinds of account cover most of the risk.

    1. Administrators nobody can name. If no one on your team can say who it belongs to and why it exists, treat it as a finding. A forgotten Administrator is an unlocked door that nobody knows about.

    2. Accounts with a role above their job. A writer who publishes their own posts needs Author, not Editor. A client who updates one page needs Editor at most. Ask whether the person could do their job one role lower. Agencies argue about whether a client should get Administrator all the time, and the least-privilege answer is almost always no.

    3. Shared logins. One account used by three people cannot be traced to any of them. When something changes, nobody can say who did it. Give each person their own account, then remove the shared one.

    One r/Wordpress thread, “Found a WordPress malware using __GA_INJ_START__ and hidden admin accounts — full incident analysis”, is a write-up of a hacked site where the attacker kept hidden administrator accounts. The author describes how the malicious code worked:

    “The malicious code maintained a list of administrator accounts that should be hidden.”

    r/Wordpress, “Found a WordPress malware using __GA_INJ_START__ and hidden admin accounts — full incident analysis”, August 2026

    What Changing A Role Breaks

    Removing access is easy to say and harder to do. A role change can stop things that quietly depend on it.

    Check these before you demote an account:

    • Scheduled work. Does a plugin run jobs as that user?
    • Authorship. Posts belong to the account. Deleting the user forces a choice about where the content goes, while demoting does not.
    • Integrations. An automation tool or API may sign in with that login.
    • Backups and updates. Some tools act as a named Administrator.

    The safe order is short. Demote first, and do not delete. Watch the site for a few days. Delete only when nothing has broken.

    Take a restore point before changing roles on a real site. If something does break, a rollback is only as good as what it can actually undo, so find that out before you need it.

    Checking It On A Schedule

    A WordPress user roles audit done once goes stale. People join, leave and change jobs, and plugins add roles.

    Keep the software current as well. WordPress 7.1.3 was released on 6 October 2026 as a maintenance and security release, and the announcement recommends updating immediately. A tidy account list says nothing about the code that guards it.

    A monthly pass is enough for most sites. Compare this month’s Administrator list with last month’s. Anything new needs an owner and a reason. Anything missing should be a change you expected.

    For agencies, this belongs in the client report as one short line: how many Administrators, and whether each one is accounted for. We wrote about what goes into a client report and what stays out. An Administrator list is a good example of a short finding that earns its place.

    A role review also fits inside a wider WordPress security audit, next to the basics in WordPress hardening.

    What This Does Not Solve

    An audit of WordPress user roles is a view of access, and it has limits.

    • It does not tell you a password is strong. A well-named account with a weak password is still a weak account.
    • It does not show activity. Core WordPress does not record who logged in or what they changed. You need an activity log for that.
    • It does not see plugin roles in full. Some plugins grant power through their own settings, not through the role name.
    • It does not see an account that hides itself. The Users screen is only as honest as the code running behind it. Compare it with a direct database query when something feels off.
    • It does not catch an account that was created and removed. If someone added an Administrator, did their work and deleted it, the list looks clean.
    • It does not cover access outside WordPress. Hosting panels, SFTP, database users and DNS each have their own accounts.
    • It does not cover access given to software. Connecting an AI tool or an automation is another kind of access and needs its own questions. We listed them in AI agent permissions on WordPress.

    A clean list from a check that skipped half these places is worse than an honest “I reviewed WordPress users only.” Say which places you covered.

    A Five-Site Audit You Can Do This Week

    Pick five sites you look after. For each one, write down:

    • The count of accounts in each role.
    • Every Administrator, with a name, an owner and a reason.
    • Any account that has not logged in for a long time, if you can tell.
    • Any account with a role above the person’s job.
    • Any shared login.

    Then choose one outcome for every account you questioned:

    1. Keep it, with an owner and a review date.
    2. Demote it one role.
    3. Test the change on a copy first, then demote.
    4. Replace a shared login with named accounts.
    5. Remove it, after a demotion period with nothing broken.
    6. Investigate it, if nobody can explain it.

    Investigate first. An Administrator nobody can explain decides whether this was a tidy-up or an incident.

    Sources And Further Reading

    The Point

    WordPress user roles are a list of who can change your site. Most sites hold more of that power than anyone has written down.

    You do not need a tool to start. You need an hour and a list.

    How many Administrators does your busiest client site have, and could you name every one of them?

  • WordPress Hacked: The First Hour, In Order

    WordPress Hacked: The First Hour, In Order

    The worst moment of a hacked site is not the discovery. It is the next ten minutes.

    You open the dashboard, you start deleting things, you reset one password, you restore a backup. Every one of those moves feels like progress. Several of them destroy the evidence you need, or lock the door while the intruder is still inside.

    Short answer: When a WordPress hacked site lands on your desk, do the first hour in this order. Write down what you see. Copy the site as it is. Cut off access, including the access that sits above WordPress. Check the backup before you trust it. Clean, update, and rotate passwords again once the site is clean. Only then ask how they got in. The order matters more than any single tool.

    What this post is, and is not: A practical response checklist for people who run or look after WordPress sites. It draws on the official WordPress.org guide to a hacked site. The order of the seven moves, and the reasons for it, are ours. It is not a malware removal service, not legal advice, and not a result from a real incident. If the site holds customer payment data or personal data, tell your host and take professional help early.

    A scan tells you what is wrong. A response tells you what to do first.

    Illustration of the seven steps in the first hour after a WordPress site is hacked: record, copy, lock access, check the backup, clean, rotate passwords again and find the cause
    Seven moves, in this order. Skipping ahead is how sites get hacked twice.

    Why Panic Cleaning Makes It Worse

    The urge is to fix what you can see. A strange admin user, a defaced page, a spam redirect. You remove it and the symptom goes away.

    That tells you very little. A visible symptom is often the last thing an intruder did, not the first. The way in is usually still open, and a copy of the way back in may already be sitting in a file you have not looked at.

    The WordPress.org guide opens with a plain instruction: stay calm, then document. It says a hack is an ambiguous word that tells you little about what happened, so the symptoms you can name are the thing to write down first.

    Panic cleaning has three common results:

    • Evidence is lost. If you delete before you copy, you cannot tell afterwards what was changed.
    • The door stays open. Cleaning files without changing access lets the same account or key back in.
    • A bad backup gets restored. A copy taken after the break-in can bring the problem straight back.

    We looked at the scanning side in WordPress Malware Scan. A scan answers “what is on this site”. This post answers the question that comes before and after it: what do I do, and in what order.

    Seven Moves For A WordPress Hacked Site

    #MoveWhy it comes here
    1Write down what you seeIt is the only record of what the site looked like before you touched it
    2Copy the site as it isYou need the evidence, and a safety net if the cleanup goes wrong
    3Lock accessStops new changes while you work
    4Check the backupA restore is only safe if the copy is older than the break-in
    5CleanRemove the cause, not just the symptom
    6Update and rotate againFixes the known weakness and replaces anything the intruder saw
    7Find how they got inWithout it, the same hole is still there

    Each move gets its own section below.

    Move One: Write It Down Before You Touch Anything

    Open a note and write four things. It takes five minutes and it is the most useful five minutes of the hour.

    • What you are seeing that tells you the site is hacked.
    • When you noticed it, with the time zone.
    • What changed recently: a new plugin, a theme edit, a new user, a host move.
    • Where the site is hosted, and who else has access.

    The WordPress.org guide lists the kind of symptoms that count as clear signs: the site is blocked by a search engine, the host has disabled it, it is flagged for distributing malware, visitors report their antivirus is warning them, you hear it is being used to attack other sites, or you see actions nobody authorised, such as new users.

    Write down which of those you actually saw. “It looks hacked” is not a symptom. “A new administrator account appeared on Tuesday” is.

    Move Two: Copy The Site As It Is

    Before you clean, take a full copy of the files and the database. Keep it somewhere that is not the same server.

    This copy is not a backup you plan to restore. It is evidence. If the cleanup breaks something, you can go back. If you later need to work out how the intruder got in, you have something to read.

    The same WordPress.org guide makes the point that even an infected copy is worth keeping. It recommends one more snapshot of the environment before the cleaning starts.

    Ask your host for logs while you are at it. Many hosts keep very little for very long, so ask early. We cannot say what yours keeps. The point is that a log you did not ask for in time is a log you will not have.

    Move Three: Lock Access, Including The Part Above WordPress

    Locking access means more than changing the admin password.

    Think of access in three layers:

    LayerExamplesOften missed because
    WordPressAdmin and editor accounts, application passwordsIt is the layer everyone remembers
    HostingControl panel, SFTP, SSH keys, database userIt sits above WordPress and plugins cannot see it
    PeopleLaptops, saved FTP passwords, shared loginsThe weak point is a machine, not the site

    For WordPress itself, the official guide suggests forcing a password reset for all users, especially administrators, and changing the secret keys in wp-config.php. Changing the keys logs everyone out, including a session the intruder may still be holding.

    For hosting, change the SFTP, control panel and database passwords, and remove any key or user you do not recognise. If you cannot name an account, treat it as a finding.

    For people, the guide recommends scanning your own computer too. In many cases the infection starts on a local machine that captured a login.

    If you cannot log in at all, the guide describes a way back through the database tools your host provides, by resetting the user record directly. That is a task for someone comfortable in the database.

    We looked at how accounts and routes get exposed in WordPress user enumeration. It is a useful read for the question “which names can an outsider see”.

    Move Four: Check The Backup Before You Trust It

    Restoring a backup feels like the safest move. It is only safe if the backup is older than the break-in.

    An intruder may have been inside for days before anything visible happened. A backup taken last night can contain exactly what you are trying to remove. The WordPress.org guide notes that you can restore a backup and move straight into the investigation, which is the careful way to read it: restore to look, not to relax.

    Three questions to ask of every backup:

    • When was it taken, compared with the first sign of trouble? Use your notes from move one.
    • Where is it stored? A backup on the same server may have been reached too.
    • Has it ever been restored on a test copy? If not, it is a file with a hopeful name. We wrote about that in verifying a WordPress backup.
    Illustration of a timeline showing that a backup taken before the first sign of the break-in is safer to restore than one taken after it
    A backup is only as clean as the day it was taken.

    Move Five: Clean The Cause, Not The Symptom

    Cleaning means replacing, not patching. The guide’s approach is practical and worth following closely.

    • Core. The folders wp-admin and wp-includes can be replaced safely with a fresh copy of the same WordPress version. The guide suggests using SFTP to upload them rather than the dashboard reinstall, because the dashboard installer tends to overwrite existing files and intruders add new ones.
    • Plugins and themes. Reinstall them from their original source. A modified file that looks normal is the whole point of a backdoor.
    • The .htaccess file. The guide calls it one of the most commonly modified files, whatever the type of infection, and notes it may sit in more than one folder.
    • Files that load on every request. index.php, header.php, footer.php and functions.php are named in the guide because a change there affects every page.
    • Users and the database. Check administrator accounts and look for anything you did not create.

    Deactivating is not removing. A file that runs when someone requests its address does not care whether the plugin around it is switched on. Delete what should not be there.

    The official guide calls finding and removing the hack the most daunting part of the whole process, and it gives no single method, because the right steps depend on the symptoms and on your own technical skill. If you are not sure, a professional clean is cheaper than a second hack.

    One practical walk-through is Ferdy Korpershoek’s video, WordPress Getting Hacked? Here’s What To Do Now, which shows a real hacked website being scanned and cleaned. The description says it uses the MalCare plugin and discloses an affiliate link, so read it as one practitioner’s method, not as a recommendation from us.

    I’ll even show you a REAL hacked website, how malware was detected, and how we removed it in just a few clicks.

    Ferdy Korpershoek, “WordPress Getting Hacked? Here’s What To Do Now” on YouTube, 18 May 2026

    Move Six: Update, Then Change The Passwords Again

    Once the site is clean, update WordPress, plugins and themes to the latest versions. The guide says older versions are more prone to being hacked, and that is where many incidents start.

    A timely example. WordPress 7.1.3 was released on 6 October 2026 as a maintenance and security release. The announcement lists seven security fixes and four bug fixes, and says that because it is a security release, updating immediately is recommended. We cannot say whether any particular hack used any of those issues, and this post does not claim that. The practical point is smaller: if your site is behind on core updates, that is the first thing to fix.

    Then change the passwords a second time. The guide is explicit about this. If you changed them when you found the hack and the intruder was still inside, the new passwords may have been seen. Change them again after the site is clean, and consider changing the database user and password as well.

    Two things are worth turning on at this point: a second factor on logins, and a short list of who has access at all. We looked at what happens when a vulnerable plugin goes unnoticed in WordPress vulnerability scanner design and at forgotten plugins in removed plugins.

    A 34-second walk-through of the seven moves. All scenes are illustrations, not a real incident.

    Move Seven: Find Out How They Got In

    This is the step that gets skipped, because the site looks fine again.

    The WordPress.org guide calls it forensics: understanding what happened and how the attacker got in, so it cannot be used again. It admits that is hard for most site owners because of missing data and technical depth.

    You can still narrow it down with what you already wrote and copied:

    • Compare the date you first saw a problem with the dates of your last plugin and theme changes.
    • Look at the accounts that were added, and when.
    • Check whether any plugin or theme was out of date, abandoned or removed from the repository.
    • Ask whether the PHP version was supported. See the WordPress PHP version problem.
    • Ask the host what they saw on their side.

    If you cannot find the cause, say so in your notes. “Unknown, access rotated, monitoring added” is an honest record. A made-up cause is worse than none.

    A site that comes back dirty is exactly what this step is for. One r/Wordpress thread, “How to fix wordpress hacked website? Can anyone guide step by step.”, starts from a site owner whose developer cleaned the site and saw the files return. Without knowing how the intruder got in, a clean-up can only hope the same door stays shut.

    “I got that checked by developee and he cleaned it but everything came back.”

    r/Wordpress, “How to fix wordpress hacked website? Can anyone guide step by step.”, October 2026

    Blacklists And Search Console Warnings

    Search engines and antivirus tools may flag a hacked site. The guide recommends registering the site with the webmaster consoles so you hear about it from them. Google Search Console also has a Manual actions report. If Google has issued a manual action against the site, you fix every affected page and then select Request Review in that report, describing what you fixed. Check the report for the exact wording that applies to your case.

    Treat that as a signal, not as proof. Not seeing a warning does not mean the site is clean, and seeing one does not tell you what is wrong. Check the site, then check the report.

    What This Does Not Solve

    A first-hour order is a way to stop making it worse. It does not make a hack impossible.

    • It does not tell you how you were hacked. Move seven narrows it down. It does not prove it.
    • It does not guarantee a clean site. Intruders can plant more than one way back in. A single clean scan is not the end.
    • It does not replace professional help. Customer data, payment data and legal duties need someone qualified.
    • It does not prevent the next one. Prevention is a different job. We measured common hardening advice in WordPress hardening.
    • It is not a result from a real incident. Nothing in this post comes from a site we cleaned.

    A response checklist is for the day it happens. A monitoring habit is for the days before it. Our write-up of WordPress security monitoring covers the second one.

    A Five-Site Check You Can Do This Week

    You do not need a hack to practise. Pick five sites you look after and answer these for each:

    1. Where would you write down what you see, and who gets the note?
    2. Can you take a full copy of files and database in under an hour?
    3. Who holds hosting, SFTP and database access, and when were those passwords last changed?
    4. Has your newest backup ever been restored on a test copy?
    5. Is the site on the latest WordPress version, with a supported PHP version?

    Then pick one outcome per site: fix it now, test it first, hand it to someone who owns it, or accept the gap with an owner and a date.

    If a site fails three of the five, start with that one. Our piece on the client report is a useful reminder of how to tell a client what you found, in the order they need it.

    Sources And Further Reading

    The Point

    A hacked site is two jobs: stop it, then understand it.

    The first hour is about doing them in that order.

    Which of your sites has no written answer to “who has access, and when did we last change it”?

  • WordPress Malware Scan: The Playbook, Step By Step

    WordPress Malware Scan: The Playbook, Step By Step

    Your site can look perfectly fine to you and still be selling pills to Google.

    That is the unsettling part of modern WordPress malware. The owner loads the homepage, sees the real homepage, and moves on. The crawler gets a different page. The client finds out when a customer asks why the search result mentions casinos.

    Short answer: a useful WordPress malware scan checks four places, not one: what the site shows to crawlers, what is sitting in the files, what is sitting in the database, and what has been set up to bring the problem back. Then it verifies every hit before it accuses anything, and it says plainly which areas it could not reach.

    What this post is, and is not: this is a practical walkthrough of how a read-only malware scan is built and how to read its output. It is not a claim that any scanner can promise a site is clean forever, and it does not describe a cleanup. Detection and removal are different jobs, and mixing them is how good files get deleted.

    An alert says something matched. A report says what it is, how sure we are, and what we could not see.

    Six numbered cards listing the WP Malware Scan steps: gateway and environment, external cloaking scan, filesystem malware sweep, database and persistence scan, verify and attribute findings, and forensic malware report. A note underneath says detection only, nothing is deleted, quarantined or edited.
    Six read-only steps, from environment to report.

    Why A Single Scan Falls Short

    Most people meet malware scanning as one button. It reads the files, matches them against a list, and prints a number.

    That catches the loud cases. It misses the quiet ones, and the quiet ones are the ones that last.

    Where can an attacker leave something behind?

    • In a PHP file that only runs when the visitor looks like a search engine.
    • In an option row in the database, stored encoded so a plain text search finds nothing.
    • In a scheduled task that quietly rewrites the payload an hour after you delete it.
    • In an administrator account that does not show up in the normal user list.
    • In an application password, which can survive a normal password reset.

    A scanner that reads files only will never see the second, third, fourth or fifth. A scanner that reads your pages only as a browser will never see the first. Each blind spot is a place to hide.

    Three Ways To Look At The Same Infection

    The same compromise looks different depending on where you stand. That is why one view alone is rarely enough.

    ViewWhat it tells youWhat it cannot prove alone
    The page as a crawler sees itWhether search engines are being shown something your visitors are notWhere the code lives, or whether anything else is planted
    The files on diskWhich files are modified, unexpected or behave like backdoorsWhether a match is malicious or a legitimate library that looks odd
    The databaseInjected content, off-domain settings, hidden accounts, odd optionsWhether the file that wrote it is still there to write it again

    A file match is a clue. A cloaked page is evidence of a symptom. A database row is a place to look. None of them is a verdict until someone reads it in context.

    Three views of one infection: the page a crawler sees, the files on disk and the database, with what each cannot prove alone
    Each view catches what the others cannot prove.

    A Small Test You Can Run Today

    Before you trust any scanner, including ours, ask a simpler question. Does your site show the same page to everyone?

    Pick five URLs: the homepage, two recent posts, one page from your sitemap, and one odd query string. Then request each one three ways and compare. Write down:

    • The page title for each request.
    • The social preview title and image.
    • Any words in the body that do not belong on your site.
    • Any script or image loaded from a host you do not recognise.

    A basic way to see it from a terminal:

    curl -s -A "Googlebot/2.1 (+http://www.google.com/bot.html)" https://example.com/ | grep -i "<title>"
    curl -s -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" https://example.com/ | grep -i "<title>"
    curl -s -A "Googlebot/2.1 (+http://www.google.com/bot.html)" "https://example.com/?g=casino" | grep -i "<title>"

    If the titles differ, or a query string produces a page that does not exist in your sitemap or your editor, stop and investigate. Two different titles are not proof of malware on their own, because some sites legitimately serve different markup to bots. But an unexplained difference is exactly what a scan is meant to surface.

    The Playbook, Step By Step

    WP Malware Scan is a Protuno security playbook, currently in beta. It runs a fixed sequence of six steps, and it only reads the site. It changes nothing: nothing is deleted, nothing is quarantined, nothing is edited. It needs the Protuno plugin on the site, because it runs its checks from inside it. It is not on the public playbooks page yet, so treat it as a new playbook that is still being rolled out.

    #StepWhat it does
    1Malware Scan Gateway And EnvironmentReads the WordPress and PHP versions, the real database table prefix, multisite status and key paths, then records what the scan can and cannot reach.
    2External Cloaking ScanRequests a bounded set of URLs as a crawler, a social preview bot and a desktop browser, and compares what each one is shown.
    3Filesystem Malware SweepLooks across the web root, WordPress core, plugins, themes, uploads, mu-plugins and drop-ins for modified, unexpected or suspicious files. Reports candidates, not verdicts.
    4Database And Persistence ScanReads options, posts, users and scheduled tasks, plus startup rules such as .htaccess, for injected content and ways back in. Select queries only.
    5Verify And Attribute FindingsReads each candidate in context and sorts it into confirmed, needs human review, or false positive. Looks for the likely way in.
    6Forensic Malware ReportWrites one honest report with a status, the evidence, and a described remediation plan. Executes nothing.

    The order matters. The first step decides what the others are allowed to claim. The next three collect candidates. The fifth step is the only one allowed to turn a candidate into a finding. The sixth cannot invent anything the earlier steps did not produce.

    Step 1: Know What You Can Reach

    Before it looks for anything, the scan records its own limits: can it read files, search content, query the database, make web requests, read the debug log. Server-level scheduled jobs and web server access logs sit outside what a WordPress plugin can read, so they are written down as coverage gaps from the start. It also reads the real table prefix instead of assuming wp_.

    Step 2: Look Like Google

    This is the step most scanners skip. The scan requests a bounded set of URLs, the homepage, up to around fifteen sitemap URLs and a few recent posts, as Googlebot, as a social preview bot and as a normal desktop browser. It compares titles, social titles and images, and checks the body for spam words such as casino, slot, pharmacy and replica. It also flags images or scripts loaded from hosts that commonly serve spam payloads.

    If the real server address is known, it asks the origin directly too, because a clean cached copy at a CDN can hide a dirty origin. If it is not known, that is recorded as a gap, not a pass. It also tries a couple of query strings as a crawler, because some spam pages only exist when a particular URL is requested.

    Step 3: Candidates Only

    The filesystem step works through the places files hide: odd folders and loose PHP in the web root, modified core files, plugins on disk but not active, PHP in uploads, mu-plugins and drop-ins, known web shell patterns and obfuscated code that runs input.

    Core files are compared with the official WordPress checksum list for the exact version installed. If that reference cannot be fetched, the step must say core integrity could not be checked. It may not compare against an empty reference and call a file modified. A false alarm that sends an agency hunting a compromise that does not exist is treated as worse than an honest “not checked”. Vendor folders and minified files are listed but never marked safe to auto-clean, because legitimate code uses the same tricks malware does.

    Step 4: The Database And The Way Back

    The database step reads every table with select queries only. In options it looks for injected scripts, off-domain site addresses and known indicator keys. It also decodes values that look like base64 and tests the decoded text, because an encoded payload contains none of the obvious markers.

    It compares the number of administrators in the database with the number in the user list; a gap means a hidden account. It lists application passwords by name, never the secret. For comments it reads only for injected markup. It does not count spam or advise emptying the spam or trash folders, because that trash is the undo path for comment cleanup.

    Step 5: Read Before You Confirm

    Every candidate is read in context and labelled confirmed, needs human, or false positive. Unsure means needs human, never confirmed. A documented list covers legitimate things that look suspicious, such as snippet plugins that store PHP in uploads on purpose.

    The step also tries to work out how the attacker got in, using the debug log where available. Without access logs, the report says attribution is limited. It maps everything that could rewrite a payload, so those can be switched off together, and if no vulnerability feed key is set it says that check was not assessed, never “no vulnerabilities”.

    Step 6: Say What Is Actually Known

    The report ends in one of four states:

    StatusWhat it means
    InfectedAt least one finding was confirmed, in a file, the database or a cloaked page.
    SuspiciousNothing confirmed, but at least one item needs a human to look.
    CleanNothing confirmed, nothing needing review, and full coverage with no gaps.
    IncompleteNothing confirmed, but some areas could not be scanned, or an earlier step did not finish.

    The last one is the important one. “No malware found in what could be scanned” is a different sentence from “your site is clean”. The playbook is written so that the word clean is earned, never assumed, and so that a layer that did not finish is never reported as clear.

    WP Malware Scan: six read-only steps.

    What A Read-Only Malware Scan Does Not Solve

    • It does not remove anything. Cleanup needs an off-site backup you hold and a human confirming each item.
    • It cannot see server-level persistence or web server access logs from inside WordPress.
    • It cannot promise the site stays clean after today.
    • It does not fix the way in. A stolen password or an outdated plugin will let the next one through.

    Questions To Ask Your Current Scanner

    Whatever you use today, these separate a scan from a signature match:

    • Does it ever request pages as a crawler, or only read files?
    • Does it check core files against the official checksums for your exact version?
    • Does it read the database, including encoded option values?
    • Does it look for hidden administrators and leftover application passwords?
    • When it finds a match, does it read it in context before calling it malware?
    • When it cannot reach a place, does it tell you, or stay silent?

    If the answer to the last one is silence, the green tick means less than it looks like.

    Which Check Are You Actually Running?

    If you need to knowThe evidence you needThe next action
    Whether search engines see spam you do notCrawler and browser responses comparedInvestigate any difference before touching files
    Whether WordPress core was alteredFiles compared against the official checksum listReinstall from a clean source, after a backup
    Whether something is hidden in the databaseOption, post and user rows read in full contextReview by a person, with a backup in hand
    Whether an old vulnerability is the doorInstalled versions matched against a vulnerability feedSee vulnerability monitoring
    Whether it will come backA map of every item that can rewrite the payloadNeutralise them together, then watch for reinfection

    Fixing Findings In The Right Order

    When a scan reports a confirmed finding, the order of work matters more than the speed.

    PriorityActionWhy this order
    1Take a full off-site backup of files and databaseYou need an undo that the attacker cannot touch
    2Note a health baselineSo you can tell whether your fix broke something
    3Reinstall legitimate core and plugin files from clean sourcesReplaces modified files without guessing
    4Neutralise everything that can rewrite the payload, togetherDelete one writer first and it regenerates
    5Move confirmed malware aside rather than deleting itQuarantine can be reversed
    6Rotate secrets and revoke rogue application passwordsA reset alone leaves some access in place
    7Prove it clean as a crawler and watch for ten to fourteen daysReinfection often shows up later

    Two practical rules sit under all of it. Restore point first, because you are about to change a live site. Re-check after each change, because the fix itself can break things. See verify a WordPress backup for why a backup you have not tested is only a hopeful file.

    A Five-Site Audit You Can Do This Week

    Pick five client sites. For each one, write down:

    • The site and who owns it.
    • Whether pages looked the same as a crawler and as a browser.
    • Whether the number of administrators matches the user list.
    • The date core files were last checked against official checksums.
    • Which areas nobody can currently see: server logs, scheduled jobs, the origin behind a CDN.

    Then choose one outcome for each item you found:

    • Fix now: confirmed, backup taken, plan agreed.
    • Test first: looks suspicious, reproduce it on a copy.
    • Replace: a plugin you cannot verify, swap it for a maintained one. See what happens when a plugin leaves the repository.
    • Remove: unused plugins and themes, which still count as attack surface.
    • Accept with an owner and a date: a known gap, written down, with a review date.
    • Investigate: unexplained difference, needs a person.

    Stopping the next break-in is half the job, so read the hardening checklist and the wp-config exposure check next.

    Why A Malware Scan Goes Stale

    A scan is a photograph. It tells you about the moment it ran.

    A clean result on Monday says nothing about a plugin installed on Tuesday, a password reused on Wednesday, or a vulnerability published on Thursday. The real value of a scan is not one report. It is the habit of running it again, comparing the answers, and noticing what changed.

    Coverage gaps matter as much as findings. A gap you wrote down is a question you can answer next month. A gap nobody recorded is where the next problem lives.

    Where Protuno Fits

    Protuno’s Rook is the Security Super Agent. WP Malware Scan is one of its playbooks, and it is read-only: it reports what it found, how sure it is and what it could not see, and a human decides what happens next. It does not clean, quarantine or delete anything. It is new and not on the public playbooks page yet; the current public security playbooks are listed on the playbooks page.

    For the wider picture of how security checks fit together, read WordPress security monitoring and WordPress security audit.

    The Point

    A site is not clean because nobody has complained yet.

    It is clean when the places that matter have been looked at, the matches have been read, and the gaps have been named.

    When did you last look at your site the way Google does?

  • WordPress Security Audit: The Playbook, Step By Step

    WordPress Security Audit: The Playbook, Step By Step

    A security grade is only useful if it tells you what it did not check.

    That sounds like a small detail. It decides whether an “A” means your site is safe, or only that the scan never reached the parts that would have lowered it.

    Short answer: a good WordPress security audit reads the site in passes, checks the site directly, tries to verify what it finds, and then gives one grade with the gaps written beside it. A grade with no gaps listed is a guess dressed as a result.

    What this post is, and is not: it is a practical look at how a graded audit is built, and what one real run on a test site produced. It is not a claim that every finding below is exploitable, or that a grade replaces a human review.

    Two cards side by side. The left card shows a security grade of C with an asterisk and a score of 78.6 out of 100. The right card lists the eight audit steps, with two marked as errored and the injection scan marked as not finished.
    A grade only means something next to the coverage behind it.

    Why A Single Grade Misleads

    Most people meet security as a number. A plugin shows a green badge. A report says 92 out of 100.

    The number is easy to read. What it hides is the question underneath it: what was actually looked at?

    A site can score well because it passed every check that ran, while the checks that matter most never ran at all. A timeout, a missing permission or a blocked feed can quietly remove half the evidence. The score still arrives.

    That is why a useful audit shows two things together: the grade, and the coverage behind it.

    Four Passes, Not One Scan

    A WordPress site can be exposed in different ways, and each way needs different evidence.

    PassWhat it looks atWhat it cannot tell you on its own
    Configuration and exposureSettings and files a visitor can reach from outside, such as logs, headers, version details and usernamesWhether any installed software has a known flaw
    Vulnerable componentsInstalled core, plugins and themes, matched against public advisory dataWhether a plugin that is not on wordpress.org has a problem
    Access controlCode paths that should check who is asking before they actWhether a flagged path can really be abused
    Injection and executionCode that handles input, uploads, requests and dynamic executionAnything, if the scan did not finish
    Four labelled columns showing configuration and exposure, vulnerable components, access control, and injection and execution, with the type of evidence each pass reads.
    Four passes, each reading a different kind of evidence.

    The audit is the work of putting these four side by side, so one pass can cover the blind spot of another. If you want the monitoring side of the same question, WordPress Security Monitoring covers how to keep checking after the first audit.

    A Small Test You Can Run Today

    Pick one staging site, or a site you are authorised to manage. Open these in a private window and write down what each one returns:

    /debug.log
    /?author=1
    /readme.html
    /wp-json/wp/v2/users

    Then look at the response headers for the home page and note which of these are present: Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, Referrer-Policy and Content-Security-Policy.

    A response is not automatically a vulnerability. It is evidence to review. The useful question is whether the exposure is needed, and who it helps.

    The Playbook, Step By Step

    WP Security Audit is a Protuno security playbook: a fixed sequence written down before it runs, so it asks the same questions every time. It only reads the site and changes nothing. There are eight steps, as they appear in the Protuno dashboard. It is not on the public playbooks page yet, so treat it as a new playbook that is still being rolled out.

    #StepWhat it does
    1Recon And FactsReads the live site: WordPress and PHP versions, theme, plugins, admin count and whether registration is open
    2Config And Exposure AuditChecks what an unauthenticated visitor can reach: debug log, headers, version disclosure, usernames, directory listing
    3Vulnerable Components AuditMatches installed core, plugins and themes against public vulnerability data
    4Security Scan, Access ControlSweeps plugin and theme code for routes that may skip an access check
    5Security Scan, Injection, Execution And Supply ChainLooks for injection, upload, request-forgery and dynamic-execution patterns
    6Verify And Triage FindingsChecks each finding against the real code before it is reported, and ranks it
    7Posture Score, A To FTurns the evidence into a grade, and marks any area it could not grade
    8ReportWrites the plain-language report with a fix for each finding

    Step six is the one that matters most, because an unverified finding is an accusation and a verified one is a task. It is also the step that errored in the real run below, so the findings in this post come from the direct checks in steps one to four, not from step six.

    What It Found On A Real Site

    The video below walks through the result of one real audit run on a WordPress test site, on 24 September 2026. Steps one to four and step seven completed. Step five did not finish, because its code scan timed out three times. Steps six and eight errored. That is part of the result, so it is shown rather than hidden.

    • Grade: C* with a score of 78.6 out of 100. The asterisk marks that one area was left ungraded.
    • Two critical findings: the debug log was publicly readable at about 1.4 MB, and the only admin account had no two-factor authentication.
    • Warnings: the admin username was revealed through the author archive, three security headers were missing, the WordPress version was disclosed in several places, plain HTTP was still served, there was no login lockout, and PHP errors were printed to visitors.
    • Low priority: in-dashboard plugin and theme installs were still allowed.
    • Passed: the checked components had no known vulnerabilities, and the core configuration checks passed, including file permissions, security salts, the file-edit lock and closed registration.
    • Not finished: step five (injection and execution) timed out three times, and steps six (verify and triage) and eight (report) errored. The report page still showed the findings from steps one to four and the score from step seven, and it states that the unfinished checks were unassessed, not clean.
    • Candidates, not confirmed: the access-control sweep flagged 65 candidates and one request-forgery candidate across 4,557 files. None was confirmed exploitable, and the report says so.
    WP Security Audit on a real test site, 24 September 2026.

    The honest part is the useful part. A scan that times out and still prints “no issues” is worse than a scan that says “this part did not run”. The grade here is lower than it might have been, and it is also more believable.

    What An Audit Does Not Solve

    An audit is one moment in time, and security monitoring covers what changes after it. Neither is a full security programme.

    It cannot, by itself, tell you whether:

    • the site is being attacked right now;
    • a plugin that is not on wordpress.org has a known flaw;
    • a flagged code path can really be abused by a real visitor;
    • a file changed after the scan finished; or
    • the site has already been compromised.

    Those are different questions that need different checks. This is also why the vulnerability monitoring question and the audit question are kept apart.

    Reading The Findings

    These are the main findings from that run, ordered by what to fix first. The smaller ones are listed in the run summary above.

    PriorityFindingWhy it matters
    CriticalPublic debug logIt can reveal paths, plugin names and errors to anyone. See WordPress Debug Log Exposed
    CriticalNo two-factor authentication on the adminOne stolen password is then enough
    WarningUsername shown by the author archiveIt hands an attacker half of a login. See WordPress REST User Enumeration
    WarningMissing security headersThey are cheap to add and easy to verify. See WordPress Security Headers
    WarningVersion disclosed in several placesRemoving one tag does not remove the others
    WarningPlain HTTP still servedVisitors and bots can still reach the site without encryption
    WarningNo login lockoutRepeated password guesses are not slowed down
    WarningPHP errors visible to visitorsDebug output belongs in a private log
    LowIn-dashboard plugin installs still allowedLow priority if you use the dashboard for updates

    Two practical rules keep this safe. Take a restore point before changing anything that can affect the site. And re-check the finding after the change: “the setting was changed” is not the same as “the exposure is gone and the site still works.” The hardening checklist lists the fixes in a suggested order.

    When The Audit Cannot See A Plugin

    One line in the report deserves its own section. Three plugins on the test site were private or off-repository, so public vulnerability feeds had nothing to say about them. The report listed them as “verify with the vendor” rather than counting them as clean.

    That distinction matters for any agency running custom or premium code. “No known vulnerabilities” can mean nothing was found, or it can mean nothing was checked. A good report tells you which. For a related risk, see WordPress Removed Plugin Risk.

    A Five-Site Audit You Can Do This Week

    Pick five sites you are responsible for. For each one, record:

    • WordPress and PHP versions
    • Whether a public debug log exists
    • Whether the author archive reveals a username
    • Which security headers are present
    • Whether the admin accounts use two-factor authentication
    • Whether failed logins are limited
    • Which plugins are private or off-repository
    • The owner, the next action and the date it was last checked

    You will learn two things quickly: which site has the biggest gap, and which of your checks would have been silently skipped. For each site, write the decision beside the finding: fix now, test first, accept with an owner and a date, or investigate. A finding without a decision is just a better-organised inbox.

    Where Protuno Fits

    In the Protuno dashboard, WP Security Audit sits under the Security agent, Rook. It is a read-only playbook with eight steps that reports a grade with its coverage beside it, and it does not change the site. Fixes stay a human decision, made with the evidence in front of you. It is new and not on the public playbooks page yet; the current public security playbooks are listed on the playbooks page.

    The Point

    A security grade should make you ask better questions, not stop asking them.

    A C that tells you what it could not check is worth more than an A that does not.

    So before asking “what is our score?”, ask the question underneath it: what did this audit actually look at, and what did it skip?

  • WordPress Security Monitoring: The Playbook, Step By Step

    WordPress Security Monitoring: The Playbook, Step By Step

    A vulnerability feed can tell you what is vulnerable. It cannot tell you which of your WordPress sites actually runs it.

    That sounds obvious. It changes the order a useful security check should happen in.

    Short answer: start with the site. Read its WordPress version, installed plugins, themes, and relevant exposure points. Match that inventory against known vulnerability information. Then validate whether the finding applies before asking anyone to act.

    Security monitoring becomes useful when it answers a question a site owner can do something with: what is running here, what needs attention, and why?

    What this post is, and is not: this is a practical monitoring playbook, not a claim that every visible endpoint is a vulnerability or that a vulnerability record proves a site is compromised. The checks below establish evidence, then give a human the context to decide what to do next.

    Three cards left to right: an advisory feed entry saying example-plugin is affected up to version 2.4.1, a site inventory showing example-plugin 2.3.0 active with no inactive plugins, and a relevant finding that this site runs an affected version.
    An advisory is a fact about software. A finding is a fact about your site.

    Why The Feed Comes Second

    Most vulnerability monitoring begins with a feed.

    A vulnerability is published. A scanner finds a matching plugin name or version. An alert arrives. That is useful as far as it goes.

    But there is a question that comes before it:

    What is actually running on this site?

    A client portfolio is rarely uniform. One site has an active plugin at one version; another has the same plugin inactive; a third has removed it. Themes differ. Core versions differ. A vulnerability can also depend on a feature, role, configuration, or route being available.

    If a tool starts with the advisory alone, it can create a queue of alerts that are technically related and operationally vague. If it starts with the site, the finding can be specific: this site is running this affected version, and this is why it needs investigation.

    That is the difference between an alert and work someone can complete. The scanner-design side of this argument is covered in WordPress Vulnerability Scanner Design: Start From The Site; this post is the wider monitoring routine around it.

    Three Ways To Look For The Same Problem

    There are three useful views of WordPress security. They overlap, but they do not answer the same question.

    ViewWhat it can tell youWhat it cannot prove on its own
    Advisory informationWhat software and versions have publicly reported issuesWhether a particular site has that software installed or is affected in practice
    Installed inventoryWhat WordPress core, plugins, and themes the site has installedWhether a known issue has been published for them, or whether they are externally exposed
    External checksWhat a visitor can observe from outside the siteWhat is installed privately inside the WordPress site

    The monitoring job is to connect the three. Each catches a blind spot left by the others.

    Three cards comparing advisory, inventory and external views of WordPress security, each showing what it can tell you and what it cannot prove alone.
    Each view catches a blind spot left by the others.

    A Small Test You Can Run Today

    Pick one staging site or a site you are authorised to manage. Make a simple record of:

    • WordPress core version
    • Active plugins and their versions
    • Installed plugins that are inactive but still present
    • Active and installed themes
    • Administrator and editor access that needs review
    • Relevant exposure points, such as REST API routes, XML-RPC, security headers, and version disclosure

    Now compare the installed versions with the vulnerability information you rely on.

    The useful result is not:

    “Plugin X has a vulnerability.”

    It is:

    “This site is running the affected version of Plugin X. Here is the advisory, the affected version range, and the next action to validate or remediate it.”

    That language makes the difference clear. It separates an industry fact from a site-specific finding.

    The Playbook, Step By Step

    Vulnerability Watch is a Protuno security playbook, currently in beta: a fixed sequence written down before it runs, so it asks the same questions every time. It only reads the site. Here is the sequence, as it appears in the dashboard.

    #StepWhat it does
    1Inventory Installed SoftwareReads WordPress core, plugins and themes, their versions and whether each is active, and flags software that is not on wordpress.org
    2Load Vulnerability FeedPulls the public advisory data to check the inventory against
    3Match Versions Against RangesCompares each installed version with the affected version range in the advisory
    4Vulnerability ReportReports what matched, and states what it could not check

    The order matters. An advisory match without the inventory is a broad warning. An inventory without advisory context is a software list. The report is only useful if it says what it could not assess as clearly as what it found.

    What It Found On A Real Site

    The video below shows the playbook running on a real WordPress site. The run was on 24 September 2026, took 1 minute 57 seconds, and completed all four steps with none failed or skipped.

    • Inventory: WordPress core, four plugins (all active, none inactive) and two themes (one active, one installed).
    • Off-repository software: three plugins that are not on wordpress.org, so public feeds do not cover them. The report marks them as needing verification with the vendor.
    • Result: 0 vulnerable, 0 high or critical. Four of seven items were assessed; three were not.
    • A feed that could not be reached: the report notes when one advisory source was unavailable and that the scan fell back to another, rather than hiding it.

    That last point is the useful one. A clean result from a check that quietly skipped half its inputs is worse than an honest “assessed four of seven”.

    Vulnerability Watch running on a real WordPress site: four steps, read only.

    What A Vulnerability Check Does Not Solve

    A vulnerability check is important. It is not a complete security programme.

    It cannot, by itself, tell you whether:

    • a core or plugin file has changed unexpectedly;
    • an installed plugin has been abandoned or removed from its source;
    • too much user information is publicly exposed;
    • XML-RPC is unnecessarily available for the site’s needs;
    • response headers are missing protections the site expects;
    • a configuration change left a route open; or
    • the site has already been compromised.

    Those are different questions, so they need focused checks. Treating every signal as one giant “security score” makes it easier to miss what the signal actually means.

    This is why Protuno keeps security work in separate playbooks rather than one generic scan. Seventeen are planned and four are built today, because vulnerability matching, integrity checks, hardening and malware response each rely on different evidence and different safe actions.

    Questions To Ask Your Current Monitoring

    Before changing tools, test the monitoring you already have. On a site you control, work through these questions:

    1. Can it identify the exact plugin and version installed on the site?
    2. Does it distinguish active software from inactive software that is still installed?
    3. Does it link a finding to the affected version range and advisory details?
    4. Does it tell you which findings need validation before an update, replacement, or mitigation?
    5. Does it test anything outside the vulnerability database?
    6. Can you show a client the evidence without translating a generic severity label first?

    If the answer to the first three is no, the system may be reading the feed more reliably than it is reading the site.

    External Exposure: Test The Site, Not The Assumption

    External checks matter because the public site may reveal things that an installed-software inventory cannot.

    For example, on an authorised test or staging site, inspect whether the following routes respond and what they disclose:

    /wp-json/wp/v2/users
    /?author=1
    /wp-sitemap-users-1.xml

    The outcome can vary by WordPress configuration, user permissions, theme behaviour, and security controls. A response is not automatically a vulnerability. It is evidence to review: are usernames disclosed in a way that creates a meaningful risk for this site, and is the exposure needed?

    Related checks are worth running in the same pass: an exposed debug log and an exposed wp-config.php neighbour. The same principle applies to version disclosure. Removing one generator tag does not prove the version is no longer visible; version references can appear elsewhere, including asset URLs. The right check verifies the observable result after a change.

    Which Check Are You Actually Running?

    If you need to know…Check the evidence that answers itTypical next action
    Is this site running known-vulnerable software?Installed inventory plus advisory matchingUpdate, replace, mitigate, or investigate the match
    Have files changed unexpectedly?Core and extension integrity checksInvestigate the change before treating it as harmless
    Is an installed extension no longer maintained?Plugin lifecycle and source-availability reviewAssess replacement and removal risk
    Are usernames or other data unnecessarily visible?External enumeration and exposure checksReduce exposure where it is not needed
    Is XML-RPC appropriate for this site?XML-RPC reachability and use-case reviewRestrict it only after checking integrations that depend on it
    Are key security headers present?HTTP response-header checksAdd or correct headers, then verify the response
    Has the site been hardened safely?Targeted hardening checks plus functional verificationConfirm the security control and the site’s normal behaviour

    The table is deliberately unglamorous. It is also how a team avoids “we ran security” becoming a phrase that means nothing when a client asks what was checked.

    Fixing Findings In The Right Order

    Not every security finding needs the same response. Start with the issues most likely to leave a site exposed, then preserve evidence and verify each change.

    PriorityWhat to addressWhy it comes here
    1Known-vulnerable software that is confirmed on the siteIt has a documented issue and a site-specific match
    2Unexpected file changes or signs of compromiseUpdating first can overwrite evidence or conceal what happened
    3Abandoned or pulled softwareIt may not receive fixes and needs a replacement or removal plan
    4Meaningful public exposureReduce unnecessary information or routes after checking legitimate dependencies
    5Hardening improvementsApply carefully, then prove the intended protection and normal site behaviour

    Two practical rules make this safer:

    • Take a restore point before changes that can affect the site.
    • Re-check the finding after the change. “The update was clicked” is not the same as “the vulnerable version is no longer installed and the site still works.”

    A Five-Site Audit You Can Do This Week

    Pick five sites you are responsible for. For each one, make a short, comparable record:

    • WordPress version
    • Installed and active plugin versions
    • Installed themes and the active theme
    • Relevant known-vulnerability matches
    • Plugins that are abandoned, removed, or no longer needed
    • File-integrity status
    • Public REST API and user-enumeration exposure
    • XML-RPC status and whether a real integration needs it
    • Security-header status
    • The owner, next action, and date the finding was re-checked

    You will learn two useful things quickly: which sites have the largest gap, and whether your monitoring is giving you enough evidence to prioritise work without opening every dashboard by hand.

    For the most important sites, do not stop at an alert. Record the remediation decision: update now, test first, replace, remove, accept temporarily with an owner and review date, or investigate. A finding without a decision is just a better-organised inbox.

    Why A Security Check Goes Stale

    A security audit is a snapshot. The site changes after it.

    Plugins update. New advisories appear. A theme changes. A new user gets a role. A client installs an integration. A route that was restricted becomes public again after a configuration change.

    So the useful question is not:

    “Did we check this site?”

    It is:

    “When did we last check what this site is actually running and exposing?”

    That is where continuous security monitoring earns its place: it makes the next relevant change visible before it becomes the thing a client finds first.

    Where Protuno Fits

    Protuno’s Rook Security agent (see the playbook list) reads what a WordPress site actually has installed, watches relevant advisory information against that inventory, and reports focused security signals in plain language. Its security playbooks are kept separate for the same reason: each part of a site’s posture needs its own evidence.

    Rook is designed to report what it found and what should happen next. It should not be treated as permission to make broad changes blindly: security improvements are strongest when the evidence, site context, and remediation decision stay connected.

    The Point

    Security monitoring does not become more useful because it produces more alerts.

    It becomes more useful when every alert is connected to what the site actually runs, what the public site actually exposes, and the next action a person can take with confidence.

    So before asking “What vulnerabilities exist?”, ask:

    “What is this WordPress site actually running right now?”

  • A WordPress Content Security Policy That Does Not Break The Site

    A WordPress Content Security Policy That Does Not Break The Site

    Short answer: deploy the four low-risk directives today, run everything else in report-only mode for a week first, and accept that a wordpress content security policy will probably keep 'unsafe-inline' for scripts unless you control the whole stack. That last part is a real constraint of the platform, not a failure of effort.

    Our own policy still has it. Here it is.

    Our CSP, including the weak part

    The live Content Security Policy header from protuno.com, showing thirteen directives with unsafe-inline highlighted in script-src and style-src, and object-src none, base-uri self, form-action self, frame-ancestors self and upgrade-insecure-requests highlighted as strong
    Read live on 7 September 2026. The green directives are doing real work and cost

    nothing. The two red ones are the compromise, and they mean this policy stops an injected external script but not an injected inline one.*

    I could have written this post without showing that. It would have been less useful.

    'unsafe-inline' in script-src substantially weakens a CSP, because the most common injection is an inline