PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
← All changes | src/component/class-notfound-lockout.php +24 -156 5.7.0 → 6.3.0 View file →
@@ -10,8 +10,9 @@
10 10 use WP_Defender\Component;
11 11 use WP_Defender\Traits\Country;
12 12 use WP_Defender\Model\Lockout_Ip;
13 13 use WP_Defender\Model\Lockout_Log;
14 +use WP_Defender\Component\Known_Bots\Known_Bots_Factory;
14 15
15 16 /**
16 17 * Handles the detection of 404 errors and manages lockouts based on configured settings.
17 18 */
@@ -55,12 +56,12 @@
55 56 // Don't interfere with WP-CLI, Cron, AJAX and legit internal requests.
56 57 if ( defender_is_wp_cli() ) {
57 58 return;
58 59 }
59 - if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
60 + if ( wp_doing_cron() ) {
60 61 return;
61 62 }
62 - if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
63 + if ( wp_doing_ajax() ) {
63 64 return;
64 65 }
65 66 if ( false === $this->model->detect_logged && is_user_logged_in() ) {
66 67 return;
@@ -93,39 +94,37 @@
93 94 $lockout_type = '';
94 95
95 96 $xss_pattern = '#(?i)(<script|javascript:|onerror=|onload=|onmouseover=|onclick=|alert\s*\(|prompt\s*\(|confirm\s*\(|document\.cookie|document\.write|eval\s*\()#';
96 97 if ( preg_match( $xss_pattern, $uri ) || preg_match( $xss_pattern, $query ) ) {
97 - // Detect it.
98 - $model = Lockout_Ip::get( $ip );
99 - $this->log_event( $ip, $uri, self::SCENARIO_404_INTELLIGENCE_XSS_ATTEMPT );
100 -
98 + $slug = $uri;
99 + $attempt_type = self::SCENARIO_404_INTELLIGENCE_XSS_ATTEMPT;
101 100 $lockout_type = self::SCENARIO_404_INTELLIGENCE_XSS_LOCKOUT;
102 - // Avoid the duplicate with the standard 404 case.
103 - $this->is_intelligence = true;
104 101 } elseif ( preg_match( '#/wp-content/(plugins|themes)/([^/]+)/#i', $uri, $m ) ) {
105 102 // Looking for requests for plugins/themes.
106 103 $entity = strtolower( $m[1] );
107 104 $slug = sanitize_file_name( $m[2] );
108 - if ( 'themes' === $entity ) {
109 - $attempt_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_THEME_ATTEMPT;
110 - $lockout_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_THEME_LOCKOUT;
111 - $path = get_theme_root() . '/' . $slug;
112 - } else {
113 - $attempt_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_PLUGIN_ATTEMPT;
114 - $lockout_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_PLUGIN_LOCKOUT;
115 - $path = WP_PLUGIN_DIR . '/' . $slug;
116 - }
105 + $path = ( 'themes' === $entity ? get_theme_root() : WP_PLUGIN_DIR ) . '/' . $slug;
106 +
117 107 // Check for the existence of the specified file path. If it doesn't exist, only then continue working on the request.
118 108 if ( ! file_exists( $path ) ) {
119 - // Detect it.
120 - $model = Lockout_Ip::get( $ip );
121 - $this->log_event( $ip, $slug, $attempt_type );
122 - // Avoid the duplicate with the standard 404 case.
123 - $this->is_intelligence = true;
109 + if ( 'themes' === $entity ) {
110 + $attempt_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_THEME_ATTEMPT;
111 + $lockout_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_THEME_LOCKOUT;
112 + } else {
113 + $attempt_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_PLUGIN_ATTEMPT;
114 + $lockout_type = self::SCENARIO_404_INTELLIGENCE_NON_EXISTENT_PLUGIN_LOCKOUT;
115 + }
124 116 }
125 117 }
126 118
127 119 if ( '' !== $lockout_type ) {
120 + // Detect it.
121 + $model = Lockout_Ip::get( $ip );
122 + $this->log_event( $ip, $slug, $attempt_type );
123 +
124 + // Avoid the duplicate with the standard 404 case.
125 + $this->is_intelligence = true;
126 +
128 127 $model = $this->record_fail_attempt( $ip, $model );
129 128 // Count the attempt.
130 129 $window = strtotime( '- ' . $this->model->timeframe . ' seconds' );
131 130
@@ -153,136 +152,17 @@
153 152 add_action( 'template_redirect', array( $this, 'process_404_detect_multiple' ) );
154 153 }
155 154
156 155 /**
157 - * Check if user-agent is looks like from googlebot.
158 - *
159 - * @param string $user_agent The user agent string to check.
160 - *
161 - * @return bool
162 - */
163 - private function is_google_ua( $user_agent = '' ): bool {
164 - if ( empty( $user_agent ) ) {
165 - $user_agent = defender_get_data_from_request( 'HTTP_USER_AGENT', 's' );
166 - if ( empty( $user_agent ) ) {
167 - return false;
168 - }
169 - $user_agent = User_Agent::fast_cleaning( $user_agent );
170 - }
171 - if ( function_exists( 'mb_strtolower' ) ) {
172 - $user_agent = mb_strtolower( $user_agent, 'UTF-8' );
173 - } else {
174 - $user_agent = strtolower( $user_agent );
175 - }
176 -
177 - if ( false !== stristr( $user_agent, 'googlebot' ) ) {
178 - return true;
179 - }
180 -
181 - return false;
182 - }
183 -
184 - /**
185 - * Check if IP is from Google, base on https://support.google.com/webmasters/answer/80553?hl=en.
186 - *
187 - * @param string $ip The IP address to check.
188 - *
189 - * @return bool
190 - */
191 - private function is_google_ip( $ip ): bool {
192 - $hostname = gethostbyaddr( $ip );
193 - // Check if this hostname has googlebot or google.com.
194 - if ( preg_match( '/\.googlebot|google\.com$/i', $hostname ) ) {
195 - $hosts = gethostbynamel( $hostname );
196 -
197 - if ( ! is_array( $hosts ) ) {
198 - return false;
199 - }
200 -
201 - // Check if this match the original ip.
202 - foreach ( $hosts as $host ) {
203 - if ( $ip === $host ) {
204 - return true;
205 - }
206 - }
207 - }
208 -
209 - return false;
210 - }
211 -
212 - /**
213 - * Checks if the user agent belongs to Bing.
214 - *
215 - * @param string $user_agent The user agent string to check.
216 - *
217 - * @return bool Returns true if the user agent is identified as belonging to Bing, false otherwise.
218 - */
219 - private function is_bing_ua( $user_agent = '' ): bool {
220 -
221 - if ( empty( $user_agent ) ) {
222 - $user_agent = defender_get_data_from_request( 'HTTP_USER_AGENT', 's' );
223 - if ( empty( $user_agent ) ) {
224 - return false;
225 - }
226 - $user_agent = User_Agent::fast_cleaning( $user_agent );
227 - }
228 -
229 - if ( function_exists( 'mb_strtolower' ) ) {
230 - $user_agent = mb_strtolower( $user_agent, 'UTF-8' );
231 - } else {
232 - $user_agent = strtolower( $user_agent );
233 - }
234 - // MSN Bot Useragent https://www.bing.com/webmaster/help/which-crawlers-does-bing-use-8c184ec0.
235 - $msn_ua = 'Bingbot|MSNBot|MSNBot-Media|AdIdxBot|BingPreview';
236 -
237 - if ( preg_match( '/' . $msn_ua . '/i', $user_agent ) ) {
238 - return true;
239 - }
240 -
241 - return false;
242 - }
243 -
244 - /**
245 - * Check if IP is from Bing, base on https://www.bing.com/webmaster/help/how-to-verify-bingbot-3905dc26.
246 - *
247 - * @param string $ip The IP address to check.
248 - *
249 - * @return bool
250 - */
251 - private function is_bing_ip( $ip ): bool {
252 - $hostname = gethostbyaddr( $ip );
253 - if ( preg_match( '/\.msnbot|msn\.com$/i', $hostname ) ) {
254 - $hosts = gethostbynamel( $hostname );
255 -
256 - if ( ! is_array( $hosts ) ) {
257 - return false;
258 - }
259 -
260 - // Check if this match the original ip.
261 - foreach ( $hosts as $host ) {
262 - if ( $ip === $host ) {
263 - return true;
264 - }
265 - }
266 - }
267 -
268 - return false;
269 - }
270 -
271 - /**
272 156 * Processes 404 detection for a single IP address.
273 157 *
274 158 * @param string $ip The IP address to process.
275 159 */
276 160 public function process_404_detect( string $ip ): void {
277 - // Check if this from Google.
278 - if ( $this->is_google_ua() && $this->is_google_ip( $ip ) ) {
161 + // Check if request is from a legitimate known bot (Google, Bing, Facebook).
162 + if ( Known_Bots_Factory::create()->is_known_bot( $ip ) ) {
279 163 return;
280 164 }
281 - // or bing.
282 - if ( $this->is_bing_ua() && $this->is_bing_ip( $ip ) ) {
283 - return;
284 - }
285 165
286 166 $uri = defender_get_data_from_request( 'REQUEST_URI', 's' );
287 167 // Strip encode.
288 168 $uri = urldecode( $uri );
@@ -444,21 +324,9 @@
444 324 * @param string $uri The URI that was accessed.
445 325 * @param string $scenario The scenario under which the event is logged.
446 326 */
447 327 public function log_event( $ip, $uri, $scenario ) {
448 - $model = new Lockout_Log();
449 - $model->ip = $ip;
450 - $user_agent = defender_get_data_from_request( 'HTTP_USER_AGENT', 's' );
451 - $model->user_agent = isset( $user_agent ) ? User_Agent::fast_cleaning( $user_agent ) : null;
452 - $model->date = time();
453 - $model->tried = $uri;
454 - $model->blog_id = get_current_blog_id();
455 -
456 - $ip_to_country = $this->ip_to_country( $ip );
457 -
458 - if ( isset( $ip_to_country['iso'] ) ) {
459 - $model->country_iso_code = $ip_to_country['iso'];
460 - }
328 + $model = Lockout_Log::create( $ip, $uri );
461 329
462 330 switch ( $scenario ) {
463 331 case self::SCENARIO_ERROR_404:
464 332 $model->type = Lockout_Log::ERROR_404;