Hardening WordPress Security and Reduce Information Disclosure
For years, WordPress is used as a platform for blogging. Last years, more and more companies have even built their website in WordPress. Unfortunately, this also means it is more often targeted by scripts, searching for their next victim. The primary reasons for a WordPress hack, are often disclosed information and outdated software components. This is applicable to the WordPress version itself and modules, like the plugins. In this article, we have a look at dealing with unwanted information disclosure, and how we can reduce revealing too much.
What are sensitive details?
As a normal user, you often won’t notice it: systems leak a lot of sensitive data. Things they should simply not share, like software package names and version numbers. They might look innocent on the first sight. Then if you consider malicious people can use these types of information to do information gather, also known as reconnaissance.
The process of information gathering is a phase which both good and bad people use to determine possible vulnerabilities. The more information shared, the easier it becomes to guess or actually know what software is being used. While it is close to impossible to hide you are running WordPress, it is possible to avoid sharing a lot other confidential pieces.
WordPress shares too much by default
Most software packages and plugins come with a readme file to explain the purpose of the software. Often this file also includes installation instructions. With open source projects, there is commonly also a LICENSE file, specifying the related software license (like GPL, MIT, BSD). Then there is the CHANGELOG file, full with details about the changes made. Version numbers are mentioned in the latter, making it is easy to find out what particular WordPress or plugin version is installed. Normally not a problem, if that data was stored only locally. With WordPress however, those files are shared and publicly available.
WordPress is sharing its version in the HTML code of your blog or website. It might look innocent, but it makes is very easy to find out who is not applying his security updates. With automated scripts searching for this information 24/7, your site is at risk.
Correction: deleting generator tag
Time to get this piece of HTML deleted on our sites. Unfortunately, the related change has to become part of your theme. This means there is a risk of your changes later being overwritten and you have to repeat it. For now, go to the editor and open up functions.php. This is your theme functions file.Add near the top the following PHP snippet:
Add near the top the following PHP snippet:
// Custom: Remove ‘generator’ meta tag
remove_action(‘wp_head’, ‘wp_generator’);
After saving, the meta tag “generator” should be gone. Check by refreshing your page and view the source code. Search for the generator tag and confirm that it is gone.
Correction: deleting text files
Next step is deleting files which might share program and version information, like README files.
Via SSH access
If you have access to the related system, the find utility will be of great help.
find /data/site -iregex “.*/(readme|license|changelog)(.md|.txt)?” -print
This command searches in the /data/site directory. It searching for files with names like readme.txt, but also LICENSE, due to use iregex (instead of regex). Ensure proper escaping, otherwise the find command will not reveal the files.
With the command above we also gather files with markdown, as that is now more common to see for documentation. It is similar to normal text files, with additional characters to denote headers and other markup.
The output of running find, might be looking something like this:
/data/site/wp-content/plugins/akismet/LICENSE.txt
/data/site/wp-content/plugins/akismet/readme.txt
/data/site/wp-content/themes/twentyfifteen/readme.txt
/data/site/wp-content/themes/twentyfifteen/genericons/LICENSE.txt
/data/site/wp-content/themes/twentyfifteen/genericons/README.md
/data/site/wp-content/themes/twentytwelve/readme.txt
/data/site/wp-content/themes/twentyfourteen/readme.txt
/data/site/wp-content/themes/twentyfourteen/genericons/LICENSE.txt
/data/site/wp-content/themes/twentyfourteen/genericons/README.txt
/data/site/wp-content/themes/twentythirteen/readme.txt
/data/site/wp-content/themes/twentythirteen/genericons/LICENSE.txt
/data/site/wp-content/themes/twentythirteen/genericons/README.txt
Manually
Without access to the system, one option might be to download all files, locally search for *.txt and delete any file which has program related data. The remove them the files from the server and upload from the local copy. Definitely not an ideal option, but it works.
Another alternative is checking common directories, like wp-content and wp-includes, and remove the related files by hand.
Prevention of information disclosure
Correction is great, now let’s take the next step and try to prevent harm from being done in the first place.
Prevent access to files
In our example, we use nginx to host a WordPress instance. With the right instructions, we can tell nginx to block all files with a specific file extension. In the case of WordPress installations, we don’t want to allow any access to backup files, SQL dumps, nor text files. So first thing is to configure a rule which blocks this:
location ~* .(?:bak|MD|sql|tar.gz|tgz|txt) { return 404; }
_Explanation: _search within all requests (~*) if something ends on .bak or .MD, etc. Any dots should be escaped, as they otherwise become part of a single character match within the regular expression. Upon a match, we return a 404 (file not found) error for that particular request. We use a 404 code, to indicate there is no such file. This way we don’t disclose there is a file, even if it actually available.
With the block above, we also restrict access to a common file robots.txt, often at the root of the website. As more specific matches have preference, we can tell nginx to allow this file.
location = /robots.txt { return 200 "User-agent: *\nAllow: /\n"; }
In this example, we return directly the file contents, instead of getting it from disk. You can also pass the request, and try to fetch it from disk, or the related WordPress installation behind it.
WordPress Security needs multi-layering
Information disclosure is just one of the many facets of information security. So if you want to securely host your website or blog with WordPress, consider taking the right steps on different layers. This way your WordPress security level can be boosted.
The example with blocking access to text files shows that defense in depth is needed. If you reinstall or update a plugin, the earlier deleted files will be back. So for each step you need to be on guard that things can reappear. Filtering out these files will help at least to prevent disclosing them later in the process.
Security is an ongoing process. So doing one action is fine, but not enough. You should continuously think about the next step to further improve the defenses of your WordPress installation. If you never did some WordPress security hardening before, here are some quick tips to look into, to prevent your WordPress installation from being hacked.
Tips for webmasters and authors:
- Subscribe to notifications, or follow blog feeds with a RSS reader
- Perform regular updates
- Check the origin of the plugins
- Delete unneeded plugins
- Check Google webmaster tools to monitor for malware infections
More advanced tips:
- If you have PHP knowledge, check what plugins do, before installing them
- Create a cronjob to delete text files in your WordPress installations
- Run a web application firewall (e.g. Naxsi for nginx)
- Perform a vulnerability scan
Got other tips to decrease the information disclosure of WordPress installations? Let it know!