WordPress websites may reveal whether a username exists on system through the author
query variable. When a web app leaks information about whether a username exists or doesn’t exist, this is called user enumeration. A common example is when you see a validation notice telling you that the username is already in use, or that the provided password is wrong (instead of the username OR password). More information can be found at OWASP.
There are a few ways to do this, basically we want to remove the ability for bots, or users, to visit our block and request an author archive page. If you’ve noticed a large number of requests to /?author=X
in your logs, then you’re being hit by bots trying to do this. Here are a few approaches I’ve used, or would suggest, to combat this.
functions.php
The universal approach involves adding a small bit of code to your WordPress theme or plugin in order to re-direct malicious requests that attempt to enumerate users. Here’s a function I commonly use to do this:
/**
* Block User Enumeration
*/
function kl_block_user_enumeration_attempts() {
if ( is_admin() ) return;
$author_by_id = ( isset( $_REQUEST['author'] ) && is_numeric( $_REQUEST['author'] ) );
if ( $author_by_id )
wp_die( 'Author archives have been disabled.' );
}
add_action( 'template_redirect', 'kl_block_user_enumeration_attempts' );
If your WordPress site is run on a server with Apache, then you can add the following rule to the .htaccess
file in your installations root folder.
# Block Attempts to Enumerate WordPress Users
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} author=\d
RewriteRule .* - [R=403,L]
MOST of the other htaccess approaches our there use a 301 redirect, but I suggest sending them to a 403 Forbidden response as suggested by WASP standards.
If your WordPress website is powered by nginx, then the following rule can be added to the location / { ... }
block of your WordPress sites’ *.conf
file to send these requests to a 403 Forbidden
error page, which seems appropriate.
# Block user enumeration
if ( $query_string ~ "author=([0-9]*)" ) { return 403; }
The full location
block would then look something like this:
location / {
index index.php;
try_files $uri $uri/ /index.php?$args;
# Block user enumeration
if ( $query_string ~ "author=([0-9]*)" ) { return 403; }
}
That should do it, using any of the above method you should stop those nasty user enumeration bots in their tracks. As always, if you have any questions let me know!