containsIP($ip)) { // From a known proxy $xIPs = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 2); return new IP($xIPs[0], true, 'HTTP_X_FORWARDED_FOR'); } else { // Not a known proxy: hard fail and bail out Syslog::single(LOG_NOTICE, 'Untrusted X-Forwarded-For header', 'WP_FAIL2BAN_AUTH_LOG', (string)$ip); do_action(__FUNCTION__.'.unknown_proxy', $ip); bail(); } } else { return $ip; } } else { /** * For plugins and themes that anonymise requests * @since 5.0.0 Refactored * @since 3.6.0 */ return new IP(WP_FAIL2BAN_REMOTE_ADDR, true, 'WP_FAIL2BAN_REMOTE_ADDR'); // @codeCoverageIgnore } } catch (InvalidIpException $e) { error_log(sprintf("%s is invalid: '%s'", $e->getMessage(), $_SERVER['REMOTE_ADDR'])); \wp_die('Internal server error', 'Internal server error', ['exit' => !defined('PHPUNIT_COMPOSER_INSTALL'), 'response' => 500]); } return null; } /** * Cache results * * @since 5.0.0 Complete refactoring * * @return IP|null */ function remote_addr(): ?IP { static $remote_addr = null; if (is_null($remote_addr)) { $remote_addr = _remote_addr(); } return $remote_addr; } /** * Catch empty usernames * * @see \wp_authenticate() * * @since 4.4.0 Add type hints * @since 4.3.4.0 Refactor to use Syslog::single * @since 4.3.0 * * @param mixed|null $user * @param string $username * @param string $password * * @return mixed|null * * @wp-f2b-soft Empty username */ function authenticate($user, string $username, string $password) // : ?mixed { if (empty($username) && isset($_POST['log'])) { Syslog::single(LOG_NOTICE, 'Empty username'); do_action(__FUNCTION__, $user, $username, $password); } return $user; } /** * Hook: wp_login * * @since 4.4.0 Add type hints, return type * @since 4.3.4.0 Refactor to use Syslog::single * @since 4.3.0 Add action * @since 4.1.0 Add REST support * @since 3.5.0 Refactored for unit testing * @since 1.0.0 * * @param string $user_login * @param mixed $user * * @return void * * @wp-f2b-good Accepted password for .* * * @codeCoverageIgnore */ function wp_login(string $user_login, $user): void { Syslog::single(LOG_INFO, "Accepted password for {$user_login}"); do_action(__FUNCTION__, $user_login, $user); } /** * Hook: wp_login_failed * * @since 4.4.0 Add type hints, return type * @since 4.3.4.0 Refactor to use Syslog::single * @since 4.3.0.5 Handle empty username * @since 4.3.0 Add action * @since 4.2.4 Add message filter * @since 4.2.0 Change username check * @since 4.1.0 Add REST support * @since 3.5.0 Refactored for unit testing * @since 1.0.0 * * @param string $username * * @return void * * @wp-f2b-hard REST authentication attempt for unknown user .* * @wp-f2b-hard XML-RPC authentication attempt for unknown user .* * @wp-f2b-soft Authentication failure for .* * @wp-f2b-soft REST authentication failure for .* * @wp-f2b-soft XML-RPC authentication failure for .* * @wp-f2b-soft Authentication attempt for unknown user .* */ function wp_login_failed(string $username): void { $username = trim($username); if (empty($username)) { $msg = 'Empty username'; $filter = '::empty'; } else { global $wp_xmlrpc_server; if (defined('REST_REQUEST')) { $msg = 'REST a'; $filter = '::REST'; } elseif ($wp_xmlrpc_server) { $msg = 'XML-RPC a'; $filter = '::XML-RPC'; } else { $msg = 'A'; $filter = ''; } $msg .= (wp_cache_get($username, 'useremail') || wp_cache_get(sanitize_user($username), 'userlogins')) ? "uthentication failure for {$username}" : "uthentication attempt for unknown user {$username}"; } $msg = apply_filters("wp_fail2ban::wp_login_failed{$filter}", $msg); Syslog::single(LOG_NOTICE, $msg); do_action(__FUNCTION__, $username); }