Hello!
There are several key best practices insofar as how to deal with security intrusions, including but not limited to restoring from backups on a clean server. In this article, I will be going over how to create an automated shell script that completes the following actions across multiple WordPress sites on your linux server :
- 1. Sanitize user and group permissions
- 2. Sanitize WordPress core admin and include files
- 3. Update WordPress Core
- 4. Update All installed plugins
- 5. Iterate through all WordPress user accounts and reset the passwords
The above actions can be implemented as part of a broader security policy when dealing with shared hosting environments where you are hosting multiple WordPress sites.
I will touch on each of the above items including the shell script snippets that are required to implement each. At the bottom of this article I will share the entirety of the shell script.
Sanitize user and group permissions for multiple WordPress sites
The purpose of this is to ensure that user and group permissions are sanitized and correct according to what is set on the root folder. First and foremost we want the bash shell script to iterate across all the WordPress sites on your server :
for obj0 in $(find /home/*/public_html/wp-config.php) do root_folder=`echo $obj0 | sed 's/\/wp-config.php//g'` USER=$(stat -c '%U' ${root_folder}) GROUP=$(stat -c '%G' ${root_folder}) chown -R ${USER}:${GROUP} $root_folder
In the above snippet, we are creating a main for loop that we will use to iterate across all wordpress sites. The above snippet is assuming that all your wordpress sites are in the /home folder. You should be able to easily modify this to other folder structures.
The next few lines within the for-loop are obtaining the full path of the root folder utilizing sed to trim off the trailing part of the path. Then we are getting the user and group that is currently set on the root folder for that particular site, and storing them in respective variables.
The last line in the above snippet is the actual change owner command in linux to recursively set the user/group permissions that we obtained in those variables.
Sanitize the wordpress core files for multiple WordPress sites
For this step we will be taking a clean source of the wordpress core files and replacing each site’s wp-admin and wp-includes folder with the clean files. This in effect will sanitize the WordPress core files.
echo "Doing ${root_folder} .. " cd $root_folder rm -rf wp-admin rm -rf wp-includes mkdir wp-admin mkdir wp-includes cp -rp /path-to/wpfiles/wp-admin/* ./wp-admin cp -rp /path-to/wpfiles/wp-includes/* ./wp-includes
In the above snippet, we are changing into the directory of the particular root folder for the particular site we are iterating within the for-loop. We then completely remove the wp-admin and wp-includes folder outright. We then recursively copy the cleanly downloaded and extracted WordPress core files and copy them into the newly re-created wp-admin and wp-includes folders. This will iterate across all sites within the for-loop.
Update WordPress core and all plugins for multiple WordPress sites
I will combine the next two steps of the shell script into this one section, because its fairly straightforward :
wp core update wp plugin update --all wp core update-db
This is where we start to use the WordPress command-line interface. Even though we are copying a freshly downloaded template of the WordPress core files, we’ll run through the core update command as the first step.
Next we want to update all installed plugins. If your concerned about compatibility problems with just blindly updating all WordPress plugins, you could focus on a list of known vulnerable plugins that may be installed on your system. This is a bit more tricky to do logistically , not technically.
Lastly we run the database update portion to implement any schema changes that the core update may have required.
Mass update all WordPress users for multiple WordPress sites using wp-cli and bash scripting
This is the more interesting of all the steps. Remember we are still in the first for-loop iteration within that bash shell script (see the first step). We want to create one more nested for-loop that will iterate across all the WordPress users of each WordPress site, and then securely change their passwords :
for obj1 in $(wp user list --field=user_login --debug=false) do pass_gen=`pwgen -cny1 25 1` echo "Resetting user ${obj1} with pass ${pass_gen} .." wp user update $obj1 --user_pass="${pass_gen}" --debug=false done
In the above snippet, we are building a nested for-loop to build a list of all WordPress users for a particular site using the wp-cli user list command. Then we iterate across all the results.
In the first command run within the nested for-loop, we are using the PWGen linux command to securely generate random strong passwords 25 characters in length.
Alternatively if you didn’t want to install the PWGen (which is in most package manager / repositories), you could use any number of the following commands to generate random passwords :
date +%s | sha256sum | base64 | head -c 32 ; echo < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo; openssl rand -base64 32 tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1 strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 30 | tr -d '\n'; echo
You get the idea, right? The last command in the first snippet of this section is the user update wp-cli comamnd. We are essentially using wp-cli to set the randomly generated password during the nested for-loop iteration of each user of each site on your server.
As part of a security or password retention policy on your server, this can easily be implemented to help facilitate and automate the process from start to finish. Alternatively in an intrusion scenario where one or more sites may be compromised, this script could prove to be an effective first response to ensure everything is sanitized and rotated. Obviously more steps need to be taken to ensure an intrusion is mitigated, but this will definitely save time.
I should also note that there is risk of data loss with this script if it is not properly tested in your environment so please take extra care and use caution when implementing on your system.
Find the full bash script below for reference :
#!/bin/sh for obj0 in $(find /home/*/public_html/wp-config.php) do root_folder=`echo $obj0 | sed 's/\/wp-config.php//g'` USER=$(stat -c '%U' ${root_folder}) GROUP=$(stat -c '%G' ${root_folder}) cd $root_folder echo "Doing ${root_folder} .. " echo $USER echo $GROUP rm -rf wp-admin rm -rf wp-includes mkdir wp-admin mkdir wp-includes cp -rp ~/wpfiles/wp-admin/* ./wp-admin cp -rp ~/wpfiles/wp-includes/* ./wp-includes wp core update --allow-root wp plugin update --all wp core update-db chown -R ${USER}:${GROUP} $root_folder for obj1 in $(wp user list --field=user_login --debug=false) do pass_gen=`pwgen -cny1 25 1` echo "Resetting user ${obj1} with pass ${pass_gen} .." wp user update $obj1 --user_pass="${pass_gen}" --debug=false done done