WordPress REST API user enumeration
What is user enumeration?
Hackers and attackers at first usually try to gather information about your website and search for possible vulnerabilities with active plugins, themes or the WP core. Part of this is the user enumeration process, when the hacker tries to find the admin username that can be later used for a bruteforce attack.
Hackers usually try to enumerate users the following way:
https://wtsn.eu/?author=1
The request will be redirected to the author’s page with the corresponding user ID:
https://wtsn.eu/author/example_admin
Since we block this type of user enumeration, this will not work. However, many websites reveal user and possible admin details.
The REST API has a /users endpoint that is useful for getting more information about the post authors. Only the authors, therefore the users with published, publicly-available post are listed. It is generally advised to use only an account with lowered capabilities for posting and commenting on a website. The admin accounts should be used for administrative tasks.
The /users endpoint for our website is the following:
https://wtsn.eu/wp-json/wp/v2/users
Since we blocked this type of user enumeration as well, it will not work. But you can check the results on the official WordPress.org site which reveals information about Barry, Douglas and many other users:
https://wordpress.org/showcase/wp-json/wp/v2/users
So how do I block this?
You can block the traditional query string based user enumeration with the following code:
if (!is_admin()) { if (preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING'])) die(); add_filter('redirect_canonical', 'stop_enum', 10, 2); } function stop_enum($redirect, $request) { if (preg_match('/\?author=([0-9]*)(\/*)/i', $request)) die(); else return $redirect; }
To unset the user endpoints of the REST API, your can use the following code:
add_filter( 'rest_endpoints', function( $endpoints ){ if ( isset( $endpoints['/wp/v2/users'] ) ) { unset( $endpoints['/wp/v2/users'] ); } if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) { unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ); } return $endpoints; });
You can add both code snippets to your theme’s function.php file or include them in a custom plugin.