147 lines
4.8 KiB
PHP
147 lines
4.8 KiB
PHP
<?php
|
|
namespace RSSSL\Security\Includes\Check404;
|
|
|
|
class Rsssl_Simple_404_Interceptor {
|
|
|
|
private $attempts = 10; // Default attempts threshold
|
|
private $time_span = 5; // Time span in seconds (5 seconds)
|
|
private $option_name = 'rsssl_404_cache';
|
|
private $notice_option = 'rsssl_404_notice_shown';
|
|
|
|
public function __construct() {
|
|
// Load the 404 test class only if the firewall has been enabled
|
|
if ( rsssl_get_option('enable_firewall') == '1' ) {
|
|
add_action( 'admin_init', array( $this, 'maybe_load_class_404_test' ), 20, 4 );
|
|
}
|
|
|
|
add_filter( 'rsssl_notices', array( $this, 'show_help_notices' ) );
|
|
|
|
if ( defined( 'rsssl_pro' ) ) {
|
|
return;
|
|
}
|
|
|
|
add_action( 'template_redirect', array( $this, 'detect_404' ) );
|
|
}
|
|
/**
|
|
* Detect and handle 404 errors.
|
|
*/
|
|
public function detect_404(): void {
|
|
if (is_404()) {
|
|
if ( get_option( $this->notice_option ) ) {
|
|
return;
|
|
}
|
|
$ip_address = $this->get_ip_address();
|
|
$current_time = time();
|
|
|
|
// Prevent the option from becoming too large
|
|
$cache = get_option($this->option_name, []);
|
|
|
|
if (!isset($cache[$ip_address])) {
|
|
$cache[$ip_address] = [];
|
|
}
|
|
|
|
$cache[$ip_address][] = $current_time;
|
|
$cache[$ip_address] = $this->clean_up_old_entries($cache[$ip_address]);
|
|
|
|
if (count($cache[$ip_address]) > $this->attempts && !get_option($this->notice_option)) {
|
|
update_option($this->notice_option, true, false);
|
|
|
|
return;
|
|
}
|
|
|
|
update_option($this->option_name, $cache, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleans up old entries based on the given timestamps.
|
|
*
|
|
* This method filters the given timestamps array and only keeps the entries where the difference between the current time
|
|
* and the timestamp is less than the specified time span.
|
|
*
|
|
* @param array $timestamps An array of timestamps.
|
|
*
|
|
* @return array The cleaned up timestamps array.
|
|
*/
|
|
private function clean_up_old_entries($timestamps): array {
|
|
$current_time = time();
|
|
return array_filter($timestamps, function($timestamp) use ($current_time) {
|
|
return ($current_time - $timestamp) < $this->time_span;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retrieves the IP address of the client.
|
|
*
|
|
* This method checks for the IP address in the following order:
|
|
* 1. HTTP_CLIENT_IP: Represents the IP address of the client if the client is a shared internet device.
|
|
* 2. HTTP_X_FORWARDED_FOR: Represents the IP address of the client if the client is accessing the server through a proxy server.
|
|
* 3. REMOTE_ADDR: Represents the IP address of the client if the client is accessing the server directly.
|
|
*
|
|
* @return string The IP address of the client.
|
|
*/
|
|
private function get_ip_address(): string {
|
|
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
|
return $_SERVER['HTTP_CLIENT_IP'];
|
|
}
|
|
|
|
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
|
}
|
|
|
|
if (!empty($_SERVER['REMOTE_ADDR'])) {
|
|
return $_SERVER['REMOTE_ADDR'];
|
|
}
|
|
|
|
return 'UNKNOWN';
|
|
}
|
|
|
|
/**
|
|
* Add a help notice for 404 detection warning.
|
|
*
|
|
* @param array $notices The existing notices array.
|
|
*
|
|
* @return array Updated notices array with 404 detection warning notice.
|
|
*/
|
|
public function show_help_notices(array $notices): array {
|
|
if (get_option($this->notice_option)) {
|
|
$message = __('We detected suspected bots triggering large numbers of 404 errors on your site.', 'really-simple-ssl');
|
|
$notice = [
|
|
'callback' => '_true_',
|
|
'score' => 1,
|
|
'show_with_options' => ['enable_404_detection'],
|
|
'output' => [
|
|
'true' => [
|
|
'msg' => $message,
|
|
'icon' => 'warning',
|
|
'type' => 'warning',
|
|
'dismissible' => true,
|
|
'admin_notice' => false,
|
|
'highlight_field_id' => 'enable_firewall',
|
|
'plusone' => true,
|
|
'url' => 'https://really-simple-ssl.com/suspected-bots-causing-404-errors/',
|
|
]
|
|
]
|
|
];
|
|
|
|
$notices['404_detection_warning'] = $notice;
|
|
}
|
|
return $notices;
|
|
}
|
|
|
|
/**
|
|
* @param $field
|
|
* @param $value
|
|
* @param $old_value
|
|
* @param $option_name
|
|
*
|
|
* @return void
|
|
*/
|
|
public function maybe_load_class_404_test() {
|
|
if ( ! get_option( 'rsssl_homepage_contains_404_resources' ) ) {
|
|
Rsssl_Test_404::get_instance();
|
|
}
|
|
}
|
|
}
|
|
|
|
new Rsssl_Simple_404_Interceptor(); |