Can I prevent enumeration of usernames on my wordpress site? I can see users at the moment using the WPScan tool.
9 Answers
A simple solution I use in a .htaccess
:
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} author=\d
RewriteRule ^ - [L,R=403]
It is similar to @jptsetme’s answer, but it works even when the query string is /?dummy&author=5
, and the search pattern for RewriteRule
is very fast: You often see a capture ([0-9]*)
in regular expressions for this. But there is no need to waste memory for the capture when you don't use the captured expression, and a match for the first character is enough, because you don't want to accept author=1b
.
Update 20.04.2017
I'm seeing more "broken" requests from people who are even too stupid to run a simple scan. The requested URLs look like this:
/?author={num:2}
So you could extend the rule above to:
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} ^author=\d+ [NC,OR]
RewriteCond %{QUERY_STRING} ^author=\{num
RewriteRule ^ - [L,R=403]
You can't.
The WPScan tool is an automated utility that takes advantage of WordPress' friendly URLs to determine usernames. It will loop through the first 10 possible IDs for authors and check the Location
header on the HTTP response to find a username.
Using http://mysite.url
for example ...
WPScan will check http://mysite.url/?author=1
. If your site is using pretty permalinks, it will return a 301 redirect with a Location
header of http://mysite.url/author/username
. If your site isn't using pretty permalinks, it will return a status of 200 (OK) instead, so WPScan will check the feed for the string "posts by username" and extract the username.
What you can do
First of all, just because someone can guess your username, doesn't mean your site is insecure. And there is really no way you can prevent someone from parsing your site in such a way.
However ...
If you are really concerned about this, I would recommend doing two things:
- Turn off pretty permalinks. This will force WPScan and similar tools to parse the content of your site for usernames rather than relying on the URL.
- Force users to set a different nickname. In the absence of a username in the URL, scanning tools will search for "posts by username" in the feed/post content instead. If you aren't putting usernames out there, then they can't be nabbed.
Another alternative is to change your author permalink rewrites. There are several ways you can do this, and you can probably find a few on this site as well.
Not tested this out thoroughly, but I think it's preferable to remove underlying resource rather than try to build walls around it on web server level. So in WP terms that would be stopping it from processing author-related query variables.
if ( ! is_admin() ) {
add_filter(
'query_vars',
function ( $public_query_vars ) {
foreach ( array( 'author', 'author_name' ) as $var ) {
$key = array_search( $var, $public_query_vars );
if ( false !== $key ) {
unset( $public_query_vars[$key] );
}
}
return $public_query_vars;
}
);
}
PS note that this will kill author archives entirely, which might or might not be appropriate paranoia level :)
You can use an .htaccess rewrite rule to prevent this disclosure, but you should also be sure to use nicknames to avoid disclosing usernames in parsable content as described by EAMann.
The following blog describes how to do it but has a typo in the rewrite rule: http://www.question-defense.com/2012/03/20/block-wordpress-user-enumeration-secure-wordpress-against-hacking
The correct rule should also remove the query string from the rewritten URL, or else you'll still disclose the username. It should look like this:
# Stop wordpress username enumeration vulnerability
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{QUERY_STRING} ^/?author=([0-9]*)
RewriteRule ^(.*)$ http://yoursite.com/somepage/? [L,R=301]
Working well for us.
I wanted to add that you can do this on nginx too. Check:
» Blocking WordPress User Enumeration on nginx - www.edwidget.name
As a side note, I wanted to prevent username enumeration on my site hosted with WP Engine, which limits users' access to low level nginx config files. They do however, have a "Redirect Rules" section in their control panel which allows you to accomplish this. After some time I managed to figure out the best configuration:
Redirect Name: // choose a description for the rewrite
Domain: // you *must* select a domain; "All Domains" will *not* work here!
Source: ^/$
Destination: /?
Then you need to show the Advanced Settings
panel...
Match args: author=([0-9]*)
Rewrite type: 301 Permanent
Et voila, your usernames are safe[r]!
I have completely blocked user enumeration from WPScan by adding the following in htaccess
# Stop wordpress username enumeration vulnerability
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{QUERY_STRING} ^/?author=([0-9]*)
RewriteRule ^(.*)$ http://yourdomain.com [L,R=301]
RewriteCond %{QUERY_STRING} author=d
RewriteRule ^ /? [L,R=301]
My professional opinion as a penetration tester for a gov't agency... it is ALWAYS worth making it harder to enumerate information about your website. Few of you will have a website that rises above the google, script kiddie hackers. We are talking about layered security and with each layer, you add time and complexity to a penetration attempt. Each layer also adds to the skillset required of the hacker. There are a few really good application firewalls available on WP. Look for those that can block IP addresses that have repeated user login attempts or 404. The idea is for your firewall to automatically block IP's that scan your website for pages that don't exist or attempt to repeatedly sign onto your site. A good feature also includes XSS and SQL Injection blocking capabilities. Consider using, All In One WP Security from Tips and Tricks HQ, Peter, Ruhul, Ivy. It has a decent interface and capability set for the novice and expert alike.
Instead of the .htaccess
route, another alternative is to add the following code to your child theme's functions.php
:
# Redirect author page to homepage
add_action( 'template_redirect', 'wpse_46469_author_page' );
function wpse_46469_author_page() {
# If the author archive page is being accessed, redirect to homepage
if ( is_author() ) {
wp_safe_redirect( get_home_url(), 301 );
exit;
}
}
Additionally, you can change the default author links that are added to the username of each page to something else (such as the homepage), by using the following:
# Replace author URL with the homepage
add_filter( 'author_link', 'wpse_46469_author_link' );
function wpse_46469_author_link() {
# Return homepage URL
return home_url();
}
I know this is an old post, but for future references I would like to add my solution as well. This is only a snippet to put in the functions.php
of your theme. It will leave everything in place and working, even the author archives, but it kills out the bad enumeration requests.
if (!is_admin()) {
if( preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING']) ) {
add_filter( 'query_vars', 'iside_remove_author_from_query_vars' );
}
add_filter('redirect_canonical', 'iside_remove_author_from_redirects', 10, 2);
}
function iside_remove_author_from_redirects($redirect, $request) {
if( !is_admin() && preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING']) ) {
add_filter( 'query_vars', 'iside_remove_author_from_query_vars' );
}
return $redirect;
}
function iside_remove_author_from_query_vars( $query_vars ) {
if( !is_admin() ) {
foreach( array( 'author', 'author_name' ) as $var ) {
$key = array_search( $var, $query_vars );
if ( false !== $key ) {
unset( $query_vars[$key] );
}
}
}
return $query_vars;
}
What it does:
- it scans the URL for something like:
author=1
- When found it will remove the author variable from the query vars so it will not be queried.
If you use permalinks, this will leave the author archives in tact. Also, if the URL will be something like: /dummy?author=1
this will just show the page for /dummy
.
Thanks to Rarst's answer to this question and https://perishablepress.com/stop-user-enumeration-wordpress/
I want to post my own vision:
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{QUERY_STRING} author=(\%|\+|\d) [NC]
RewriteRule (.*) $1? [L]
First line detects only home page. I'll explain why. This "user-enumeration" feature works only on home page, so no need to rewrite all urls.
Next we look for author=
query string. It's obvious.
Finally, we just show original page without any blocks, redirects (301, 302) or bans (403). Shouldn't it act like a page with any other useless parameter?
-
By targeting only the homepage you are leaving index.php out, so enumeration is still possible.– guidodCommented Oct 20, 2023 at 9:36