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
defender-security / src / model / setting / class-user-agent-lockout.php

class-user-agent-lockout.php in Defender Security – Malware Scanner, Login Security & Firewall 6.3.0, at src/model/setting/class-user-agent-lockout.php

506 lines 12.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handle user agent lockout settings.
4 *
5 * @package WP_Defender\Model\Setting
6 */
7
8 namespace WP_Defender\Model\Setting;
9
10 use Calotes\Model\Setting;
11 use WP_Defender\Component\User_Agent as User_Agent_Service;
12
13 /**
14 * Model for user agent lockout settings.
15 *
16 * Since with v5.4.0 blocklisted UA has groups:
17 * 'Blocklist Presets' is associated with the properties $blocklist_presets & $blocklist_preset_values,
18 * 'Scripts Presets' is associated with the properties $script_presets & $script_preset_values,
19 * 'Custom User Agents' is associated with the property $blacklist.
20 */
21 class User_Agent_Lockout extends Setting {
22 const BOT_LOCKOUT_TYPE_ALLOWED = array( 'temporary', 'permanent' );
23 const BOT_LOCKOUT_DURATION_UNIT_ALLOWED = array( 'seconds', 'minutes', 'hours' );
24
25 /**
26 * Option name.
27 *
28 * @var string
29 */
30 protected $table = 'wd_user_agent_settings';
31
32 /**
33 * Is module enabled?
34 *
35 * @var bool
36 * @defender_property
37 */
38 public bool $enabled = false;
39
40 /**
41 * Blacklist User Agents.
42 *
43 * @var string
44 * @defender_property
45 */
46 public $blacklist = '';
47
48 /**
49 * Whitelist User Agents.
50 *
51 * @var string
52 * @defender_property
53 */
54 public $whitelist = '';
55
56 /**
57 * Message to display for user agent lockout.
58 *
59 * @var string
60 * @defender_property
61 * @sanitize sanitize_textarea_field
62 */
63 public $message = '';
64
65 /**
66 * Is empty headers allowed?
67 *
68 * @var bool
69 * @defender_property
70 */
71 public $empty_headers = false;
72
73 /**
74 * Is blocklist presets enabled?
75 *
76 * @var bool
77 * @defender_property
78 */
79 public $blocklist_presets = true;
80
81 /**
82 * The list of Blocklist presets.
83 *
84 * @var array
85 * @defender_property
86 */
87 public $blocklist_preset_values = array();
88
89 /**
90 * Is script presets enabled?
91 *
92 * @var bool
93 * @defender_property
94 */
95 public $script_presets = false;
96
97 /**
98 * The list of Script presets.
99 *
100 * @var array
101 * @defender_property
102 */
103 public $script_preset_values = array();
104
105 /**
106 * Is malicious bot enabled?
107 *
108 * @var bool
109 * @defender_property
110 */
111 public bool $malicious_bot_enabled = false;
112
113 /**
114 * How the lock is going to be, if we choose permanent, then their IP will be blacklisted.
115 *
116 * @var string
117 * @defender_property
118 * @rule in[temporary,permanent]
119 */
120 public $malicious_bot_lockout_type = 'temporary';
121
122 /**
123 * Duration for the lockout.
124 *
125 * @var int
126 * @defender_property
127 * @rule required|integer
128 */
129 public $malicious_bot_lockout_duration = 300;
130
131 /**
132 * Duration unit.
133 *
134 * @var string
135 * @defender_property
136 * @rule in[seconds,minutes,hours]
137 */
138 public $malicious_bot_lockout_duration_unit = 'seconds';
139
140 /**
141 * The message to show on frontend when a malicious bot is triggered.
142 *
143 * @var string
144 * @defender_property
145 * @sanitize sanitize_textarea_field
146 */
147 public $malicious_bot_message = '';
148
149 /**
150 * Is fake bots enabled?
151 *
152 * @var bool
153 * @defender_property
154 */
155 public bool $fake_bots_enabled = false;
156
157 /**
158 * How the lock is going to be, if we choose permanent, then their IP will be blacklisted.
159 *
160 * @var string
161 * @defender_property
162 * @rule in[temporary,permanent]
163 */
164 public $fake_bots_lockout_type = 'temporary';
165
166 /**
167 * Duration for the lockout.
168 *
169 * @var int
170 * @defender_property
171 * @rule required|integer
172 */
173 public $fake_bots_lockout_duration = 300;
174
175 /**
176 * Duration unit.
177 *
178 * @var string
179 * @defender_property
180 * @rule in[seconds,minutes,hours]
181 */
182 public $fake_bots_lockout_duration_unit = 'seconds';
183
184 /**
185 * The message to show on frontend when a fake bot is triggered.
186 *
187 * @var string
188 * @defender_property
189 * @sanitize sanitize_textarea_field
190 */
191 public $fake_bots_message = '';
192
193 /**
194 * Old properties of the Bot Trap option for backward compatibility. We can remove them in future versions.
195 * Is bot trap enabled?
196 *
197 * @var bool
198 * @defender_property
199 */
200 public bool $bot_trap_enabled = false;
201
202 /**
203 * How the lock is going to be, if we choose permanent, then their IP will be blacklisted.
204 *
205 * @var string
206 * @defender_property
207 * @rule in[temporary,permanent]
208 */
209 public $bot_trap_lockout_type = 'temporary';
210
211 /**
212 * Duration for the lockout.
213 *
214 * @var int
215 * @defender_property
216 * @rule required|integer
217 */
218 public $bot_trap_lockout_duration = 300;
219
220 /**
221 * Duration unit.
222 *
223 * @var string
224 * @defender_property
225 * @rule in[seconds,minutes,hours]
226 */
227 public $bot_trap_lockout_duration_unit = 'seconds';
228
229 /**
230 * Rules for validation.
231 *
232 * @var array
233 */
234 protected $rules = array(
235 array(
236 array(
237 'enabled',
238 'empty_headers',
239 'blocklist_presets',
240 'script_presets',
241 'malicious_bot_enabled',
242 'bot_trap_enabled',
243 'fake_bots_enabled',
244 ),
245 'boolean',
246 ),
247 array( array( 'malicious_bot_lockout_type' ), 'in', self::BOT_LOCKOUT_TYPE_ALLOWED ),
248 array( array( 'malicious_bot_lockout_duration_unit' ), 'in', self::BOT_LOCKOUT_DURATION_UNIT_ALLOWED ),
249 array( array( 'fake_bots_lockout_type' ), 'in', self::BOT_LOCKOUT_TYPE_ALLOWED ),
250 array( array( 'fake_bots_lockout_duration_unit' ), 'in', self::BOT_LOCKOUT_DURATION_UNIT_ALLOWED ),
251 );
252
253 /**
254 * Get default values.
255 *
256 * @return array
257 */
258 public function get_default_values(): array {
259 // Allowled User Agents.
260 $whitelist = "a6-indexer\nadsbot-google\naolbuild\napis-google\nbaidu\nbingbot\nbingpreview";
261 $whitelist .= "\nbutterfly\ncloudflare\nchrome\nduckduckbot\nembedly\nfacebookexternalhit\nfacebot\ngoogle page speed";
262 $whitelist .= "\ngooglebot\nia_archiver\nlinkedinbot\nmediapartners-google\nmsnbot\nnetcraftsurvey";
263 $whitelist .= "\noutbrain\npinterest\nquora\nslackbot\nslurp\ntweetmemebot\ntwitterbot\nuptimerobot";
264 $whitelist .= "\nurlresolver\nvkshare\nw3c_validator\nwordpress\nwp rocket\nyandex";
265 $message = esc_html__( 'You have been blocked from accessing this website.', 'defender-security' );
266
267 return array(
268 'message' => $message,
269 'malicious_bot_message' => $message,
270 'fake_bots_message' => $message,
271 'whitelist' => $whitelist,
272 // Blocked User Agents.
273 'blacklist' => '',
274 'blocklist_presets' => true,
275 'blocklist_preset_values' => array( 'mj12bot', 'dotbot' ),
276 'script_presets' => false,
277 'script_preset_values' => array(),
278 );
279 }
280
281 /**
282 * Initializes the object by setting default values for the message, whitelist, and blacklist properties.
283 * This function retrieves the default values from the get_default_values() method and assigns them to the
284 * corresponding properties of the object.
285 *
286 * @return void
287 */
288 protected function before_load(): void {
289 $default_values = $this->get_default_values();
290 $this->message = $default_values['message'];
291 $this->whitelist = $default_values['whitelist'];
292 $this->blacklist = $default_values['blacklist'];
293 $this->blocklist_presets = $default_values['blocklist_presets'];
294 $this->blocklist_preset_values = $default_values['blocklist_preset_values'];
295 $this->script_presets = $default_values['script_presets'];
296 $this->script_preset_values = $default_values['script_preset_values'];
297 $this->malicious_bot_message = $default_values['malicious_bot_message'];
298 $this->fake_bots_message = $default_values['fake_bots_message'];
299 }
300
301 /**
302 * Checks if the User Agent Lockout feature is active.
303 *
304 * @return bool Returns true if the User Agent Lockout feature is active, false otherwise.
305 */
306 public function is_active(): bool {
307 $enable = apply_filters(
308 'wd_user_agents_enable',
309 $this->enabled
310 );
311
312 return is_bool( $enable ) ? $enable : (bool) $enable;
313 }
314
315 /**
316 * Get a list of blocklisted or allowlisted data.
317 *
318 * @param string $type blocklist|allowlist.
319 * @param bool $lower Whether to convert the list to lowercase.
320 *
321 * @return array
322 */
323 public function get_lockout_list( $type = 'blocklist', $lower = true ): array {
324 $data = 'blocklist' === $type ? $this->blacklist : $this->whitelist;
325 $data = trim( $data );
326
327 if ( '' === $data ) {
328 return array();
329 }
330
331 $arr = preg_split( "/\r\n|\n|\r/", $data );
332 if ( ! is_array( $arr ) ) {
333 return array();
334 }
335
336 if ( $lower ) {
337 $arr = array_map(
338 function ( $value ) {
339 return strtolower( trim( $value ) );
340 },
341 $arr
342 );
343 } else {
344 $arr = array_map( 'trim', $arr );
345 }
346
347 return array_unique( $arr );
348 }
349
350 /**
351 * Define settings labels.
352 *
353 * @return array
354 */
355 public function labels(): array {
356 return array(
357 'enabled' => self::get_module_name(),
358 'message' => esc_html__( 'Message', 'defender-security' ),
359 'blacklist' => esc_html__( 'Blocklist', 'defender-security' ),
360 'whitelist' => esc_html__( 'Allowlist', 'defender-security' ),
361 'empty_headers' => esc_html__( 'Empty Headers', 'defender-security' ),
362 );
363 }
364
365 /**
366 * Get the access status of this UA.
367 *
368 * @param string $ua User Agent.
369 *
370 * @return array
371 */
372 public function get_access_status( $ua ): array {
373 $blocklist = $this->get_all_selected_blocklist_ua();
374 $allowlist = $this->get_lockout_list( 'allowlist' );
375
376 // Escape regex special characters in each item before building the pattern.
377 $blocklist_escaped = array_map( 'preg_quote', $blocklist, array_fill( 0, count( $blocklist ), '#' ) );
378 $allowlist_escaped = array_map( 'preg_quote', $allowlist, array_fill( 0, count( $allowlist ), '#' ) );
379
380 $blocklist_regex_pattern = '#' . implode( '|', $blocklist_escaped ) . '#i';
381 $allowlist_regex_pattern = '#' . implode( '|', $allowlist_escaped ) . '#i';
382
383 $blocklist_match = count( $blocklist ) > 0 && 1 === preg_match( $blocklist_regex_pattern, $ua );
384 $allowlist_match = count( $allowlist ) > 0 && 1 === preg_match( $allowlist_regex_pattern, $ua );
385
386 if ( ! $blocklist_match && ! $allowlist_match ) {
387 return array( 'na' );
388 }
389
390 $result = array();
391
392 // Check blocklist first - if it matches, add 'banned'.
393 if ( $blocklist_match ) {
394 $result[] = 'banned';
395 }
396
397 // Check allowlist - if it matches, add 'allowlist'.
398 if ( $allowlist_match ) {
399 $result[] = 'allowlist';
400 }
401
402 return $result;
403 }
404
405 /**
406 * Add an UA to the list.
407 *
408 * @param string $ua User agent name.
409 * @param string $collection blocklist|allowlist.
410 *
411 * @return void
412 */
413 public function add_to_list( $ua, $collection ) {
414 if ( 'blocklist' === $collection ) {
415 if ( User_Agent_Service::is_blocklist_presets( $ua ) ) {
416 $this->blocklist_presets = true;
417 $this->blocklist_preset_values[] = $ua;
418 } elseif ( User_Agent_Service::is_script_presets( $ua ) ) {
419 $this->script_presets = true;
420 $this->script_preset_values[] = $ua;
421 } else {
422 $this->blacklist = $this->push_ua_to_list( $ua, $collection );
423 }
424 } elseif ( 'allowlist' === $collection ) {
425 $this->whitelist = $this->push_ua_to_list( $ua, $collection );
426 }
427
428 $this->save();
429 }
430
431 /**
432 * Push the UA to either blocklist (only in 'Custom User Agents') or allowlist
433 *
434 * @param string $ua User agent name.
435 * @param string $collection List type i.e. blocklist or allowlist.
436 *
437 * @return string List as string format with UA delimited with newline character.
438 */
439 private function push_ua_to_list( string $ua, string $collection ): string {
440 $arr = $this->get_lockout_list( $collection, false );
441 $arr[] = trim( $ua );
442 $arr = array_unique( $arr );
443
444 return implode( PHP_EOL, $arr );
445 }
446
447 /**
448 * Filter the UAs, as we use a textarea to submit, so it can contain some un-valid UAs.
449 */
450 protected function after_validate(): void {
451 $this->blacklist = implode(
452 PHP_EOL,
453 array_filter( $this->get_lockout_list( 'blocklist' ), 'strlen' )
454 );
455
456 $this->whitelist = implode(
457 PHP_EOL,
458 array_filter( $this->get_lockout_list( 'allowlist' ), 'strlen' )
459 );
460 }
461
462 /**
463 * Get the module name.
464 *
465 * @return string
466 */
467 public static function get_module_name(): string {
468 return esc_html__( 'User Agent Banning', 'defender-security' );
469 }
470
471 /**
472 * Get the module state.
473 *
474 * @param bool $flag Module state.
475 *
476 * @return string
477 */
478 public static function get_module_state( $flag ): string {
479 return $flag ? esc_html__( 'active', 'defender-security' ) : esc_html__( 'inactive', 'defender-security' );
480 }
481
482 /**
483 * Get the list of all selected blocklisted UA values.
484 *
485 * @param bool $lower Whether to convert the list to lowercase.
486 *
487 * @return array
488 */
489 public function get_all_selected_blocklist_ua( $lower = true ): array {
490 $blocklist_custom = $this->get_lockout_list( 'blocklist', $lower );
491 $blocklist_presets = $this->blocklist_presets ? $this->blocklist_preset_values : array();
492 $script_presets = $this->script_presets ? $this->script_preset_values : array();
493
494 return array_merge( $blocklist_custom, $blocklist_presets, $script_presets );
495 }
496
497 /**
498 * Return the module slug.
499 *
500 * @return string
501 */
502 public static function get_module_slug(): string {
503 return 'ua-lockout';
504 }
505 }
506