Viewing File: /usr/local/sitepad/editor/site-data/plugins/loginizer-security/main/userenum.php
<?php
namespace LoginizerPro;
if(!defined('ABSPATH')){
die('HACKING ATTEMPT!');
}
class UserEnum {
// Block unauthenticated requests to the WP REST API users endpoint
public static function block_users_request($response, $handler, $request){
global $loginizer;
if(empty($loginizer['user_enum_disable_rest_users'])){
return $response;
}
if(is_user_logged_in()){
return $response;
}
$route = $request->get_route();
if(strpos($route, '/wp/v2/users') !== false){
return new \WP_Error('loginizer_user_enum', __('You are not allowed to access this resource.', 'loginizer'), ['status' => 403]);
}
return $response;
}
// Block ?author=N and ?author_name= enumeration via 404 and prevent canonical redirect (unauthenticated only)
public static function disable_author_enum($query){
global $loginizer;
if(empty($loginizer['user_enum_disable_author_enum'])){
return;
}
if(is_admin() || is_user_logged_in()){
return;
}
$author = $query->get('author');
$author_name = $query->get('author_name');
if((!empty($author) && is_numeric($author)) || !empty($author_name)){
// Prevent redirect_canonical from leaking the username via /author/username/
remove_action('template_redirect', 'redirect_canonical', 10);
// Clear the author query vars so the author archive never resolves
$query->set('author', '');
$query->set('author_name', '');
// Force a 404 response
$query->set_404();
$query->is_404 = true;
$query->is_author = false;
}
}
// Mask login form errors regardless of brute force status
public static function hide_login_errors($error){
global $loginizer;
if(empty($loginizer['user_enum_hide_login_errors'])){
return $error;
}
if(!is_wp_error($error)){
return $error;
}
$codes = $error->get_error_codes();
// Already masked
if(in_array('invalid_userpass', $codes, true)){
return $error;
}
if(!self::mask_codes($error, $codes)){
return $error;
}
$error->add('invalid_userpass', '<b>ERROR:</b> ' . __('Invalid username or password.', 'loginizer'));
return $error;
}
// Removes the error codes which tell the visitor whether the username exists
// Returns true only if one of them was actually there, so that the callers do
// not replace errors like empty_username, or a logged out / registered notice,
// with an invalid credentials error
private static function mask_codes($error, $codes){
$mask = ['invalid_username', 'incorrect_password', 'invalid_email'];
$masked = false;
foreach($mask as $c){
if(in_array($c, $codes, true)){
$masked = true;
}
$error->remove($c);
}
return $masked;
}
// Mask errors during authenticate when brute force is off
public static function mask_authenticate($user, $username, $password){
global $loginizer;
if(empty($loginizer['user_enum_hide_login_errors'])){
return $user;
}
if(!is_wp_error($user)){
return $user;
}
$codes = $user->get_error_codes();
// Already masked
if(in_array('invalid_userpass', $codes, true)){
return $user;
}
if(!self::mask_codes($user, $codes)){
return $user;
}
$user->add('invalid_userpass', '<b>ERROR:</b> ' . __('Invalid username or password.', 'loginizer'));
return $user;
}
// Mask lost password form errors
// Hooked to the lost_password action as WordPress adds the invalidcombo error
// after the lostpassword_errors filter. WP_Error is an object, so what we
// remove / add here is what wp-login.php is about to render
public static function hide_lostpass_errors($error = null){
global $loginizer;
if(empty($loginizer['user_enum_hide_lostpass_errors'])){
return $error;
}
// The $errors param was added to this action in WordPress 5.1
if(!is_wp_error($error)){
return $error;
}
// These codes tell the visitor whether the account exists
$mask = ['invalidcombo', 'invalid_email', 'invalid_combo', 'email_not_registered'];
$codes = $error->get_error_codes();
$masked = 0;
foreach($mask as $c){
if(in_array($c, $codes, true)){
$masked = 1;
}
$error->remove($c);
}
// Nothing that leaks, so do not touch the form
if(empty($masked)){
return $error;
}
$generic = __('If that account exists, an email has been sent with instructions to reset the password.', 'loginizer');
// 'message' so WordPress shows it as a notice and not as a red error
$error->add('loginizer_user_enum_lostpass', $generic, 'message');
return $error;
}
// Strip author info from oEmbed response data
public static function disable_oembed_author($data, $post, $width, $height){
global $loginizer;
if(empty($loginizer['user_enum_disable_oembed_author'])){
return $data;
}
if(is_array($data)){
unset($data['author_name'], $data['author_url']);
}
return $data;
}
// Strip author name from oEmbed REST XML/JSON responses
public static function oembed_strip_author($result, $server, $request, $response){
global $loginizer;
if(empty($loginizer['user_enum_disable_oembed_author'])){
return $result;
}
$route = $request->get_route();
if(strpos($route, '/oembed/1.0/embed') === false){
return $result;
}
if(!($response instanceof \WP_REST_Response)){
return $result;
}
$data = $response->get_data();
if(is_array($data)){
unset($data['author_name'], $data['author_url']);
$response->set_data($data);
}
return $result;
}
}Back to Directory