PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.5.1
Defender Security – Malware Scanner, Login Security & Firewall v5.5.1
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 / controller / class-bot-trap.php

class-bot-trap.php in Defender Security – Malware Scanner, Login Security & Firewall 5.5.1, at src/controller/class-bot-trap.php

254 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles bot trap functionality.
4 *
5 * @package WP_Defender\Controller
6 */
7
8 namespace WP_Defender\Controller;
9
10 use WP_Defender\Controller;
11 use WP_Defender\Component\Blacklist_Lockout;
12 use WP_Defender\Component\Bot_Trap as Bot_Trap_Component;
13 use WP_Defender\Component\Known_Bots\Known_Bots_Factory;
14 use WP_Defender\Model\Lockout_Ip;
15 use WP_Defender\Traits\IP;
16
17 /**
18 * Handles operations to insert a weekly rotating hash URL into the footer,
19 * and blocking IP addresses that access this URL.
20 */
21 class Bot_Trap extends Controller {
22 use IP;
23
24 const URL_QUERY = 'wpdef-bot-trap-url';
25
26 /**
27 * Service for handling logic.
28 *
29 * @var Bot_Trap_Component
30 */
31 protected $service;
32
33 /**
34 * Constructor for the Bot_Trap class.
35 * Initializes the service and sets up necessary hooks.
36 *
37 * @param Bot_Trap_Component $service The service instance for bot trap functionality.
38 */
39 public function __construct( Bot_Trap_Component $service ) {
40 $this->service = $service;
41
42 if ( $this->service->is_enabled() ) {
43 add_action( 'init', array( $this, 'init' ) );
44 add_action( 'wpdef_rotate_bot_trap_secret_hash', array( $this, 'rotate_hash' ) );
45 add_filter( 'query_vars', array( $this, 'add_query_var' ) );
46 add_action( 'after_switch_theme', array( $this, 'flush_rewrite' ) );
47
48 $service = wd_di()->get( Blacklist_Lockout::class );
49 $ip = $this->get_user_ip();
50 if ( ! $service->are_ips_whitelisted( $ip ) ) {
51 add_action( 'wp_footer', array( $this, 'inject_footer' ) );
52 add_action( 'login_footer', array( $this, 'inject_footer' ) );
53 add_action( 'template_redirect', array( $this, 'handle_hash_url' ) );
54 }
55 }
56 }
57
58 /**
59 * Initializes the bot trap functionality.
60 * Schedules a weekly cron job to rotate the hash and registers a rewrite rule.
61 */
62 public function init() {
63 $this->schedule_cron();
64
65 if ( ! $this->service->get_hash() ) {
66 $this->service->rotate_hash();
67 }
68
69 $this->flush_rewrite();
70 $this->service->handle_robots_txt();
71 }
72
73 /**
74 * Schedules a weekly cron job to rotate the bot trap hash.
75 * This ensures that the bot trap URL changes weekly.
76 */
77 public function schedule_cron() {
78 if ( ! wp_next_scheduled( 'wpdef_rotate_bot_trap_secret_hash' ) ) {
79 wp_schedule_event( time(), 'weekly', 'wpdef_rotate_bot_trap_secret_hash' );
80 }
81 }
82
83 /**
84 * Registers a rewrite rule for the bot trap URL.
85 * The URL will be in the format: /{hash}/
86 * where {hash} is a 16-character hexadecimal string.
87 */
88 public function register_rewrite_rule() {
89 $hash = $this->service->get_hash();
90 add_rewrite_rule( "^{$hash}/?$", 'index.php?' . self::URL_QUERY . '=' . $hash, 'top' );
91 }
92
93 /**
94 * Rotates the bot trap hash and flushes rewrite rules.
95 */
96 public function rotate_hash() {
97 $this->service->rotate_hash();
98 }
99
100 /**
101 * Adds a query variable for the bot trap URL.
102 * This allows us to capture the hash from the URL.
103 *
104 * @param array $vars Existing query variables.
105 * @return array Modified query variables.
106 */
107 public function add_query_var( $vars ) {
108 $vars[] = self::URL_QUERY;
109 return $vars;
110 }
111
112 /**
113 * Handles the bot trap URL when accessed.
114 * If the hash in the URL matches the stored hash, block the IP.
115 * Otherwise, it will do nothing.
116 */
117 public function handle_hash_url() {
118 $used_hash = get_query_var( self::URL_QUERY );
119 $valid_hash = $this->service->get_hash();
120
121 if ( $used_hash === $valid_hash ) {
122 $known_bots = Known_Bots_Factory::create();
123 $bot_ips = $known_bots->get_all_bot_ips();
124
125 // Flatten 2D array into a single array.
126 $flattened_bot_ips = array();
127 foreach ( $bot_ips as $ips ) {
128 foreach ( $ips as $ip ) {
129 $flattened_bot_ips[] = $ip;
130 }
131 }
132
133 $model = $this->service->model;
134 $ips = $this->service->get_user_ip();
135
136 foreach ( $ips as $ip ) {
137 // Skip if the IP is a known bot IP.
138 if ( $this->is_ip_in_format( $ip, $flattened_bot_ips ) ) {
139 continue;
140 }
141
142 $lockout_model = Lockout_Ip::get( $ip );
143 $remaining_time = 0;
144 if ( 'permanent' === $model->bot_trap_lockout_type ) {
145 $lockout_model->attempt = 0;
146 $lockout_model->meta['login'] = array();
147 $lockout_model->meta['nf'] = array();
148 $lockout_model->save();
149
150 do_action( 'wd_blacklist_this_ip', $ip );
151 } else {
152 $lockout_model->status = Lockout_Ip::STATUS_BLOCKED;
153 $lockout_model->lock_time = time();
154
155 $this->service->create_blocked_lockout(
156 $lockout_model,
157 $model->bot_trap_message,
158 strtotime( '+' . $model->bot_trap_lockout_duration . ' ' . $model->bot_trap_lockout_duration_unit )
159 );
160
161 $remaining_time = $lockout_model->remaining_release_time();
162 }
163
164 // Need to create a log.
165 $this->service->log_event( $ip, $used_hash, Bot_Trap_Component::SCENARIO_BOT_TRAP );
166
167 wd_di()->get( Firewall::class )->actions_for_blocked(
168 $model->bot_trap_message,
169 $remaining_time,
170 Bot_Trap_Component::SCENARIO_BOT_TRAP,
171 $ips
172 );
173 }
174 }
175 }
176
177 /**
178 * Injects the bot trap URL into the footer of frontend pages.
179 * This URL is hidden.
180 */
181 public function inject_footer() {
182 if ( is_admin() ) {
183 return;
184 }
185
186 $hash = $this->service->get_hash();
187 echo '<div style="display:none;"><a href="' . esc_url( home_url( "/{$hash}" ) ) . '">Secret Link</a></div>';
188 }
189
190 /**
191 * Flushes the rewrite rules to ensure the new bot trap URL is recognized.
192 */
193 public function flush_rewrite() {
194 $this->register_rewrite_rule();
195 flush_rewrite_rules();
196 }
197
198 /**
199 * Delete all the data & the cache.
200 */
201 public function remove_data() {
202 // Remove the bot trap hash from options.
203 delete_site_option( Bot_Trap_Component::URL_HASH_KEY );
204
205 $this->service->remove_rule();
206
207 // Flush rewrite rules to remove the bot trap URL.
208 flush_rewrite_rules();
209 }
210
211 /**
212 * Exports strings.
213 *
214 * @return array An array of strings.
215 */
216 public function export_strings(): array {
217 return array();
218 }
219
220 /**
221 * Converts the object data to an array.
222 *
223 * @return array An array representation of the object.
224 */
225 public function to_array(): array {
226 return array();
227 }
228
229 /**
230 * Imports data into the model.
231 *
232 * @param array $data Data to be imported into the model.
233 *
234 * @throws Exception If table is not defined.
235 */
236 public function import_data( array $data ) {
237 }
238
239 /**
240 * Removes settings for all submodules.
241 */
242 public function remove_settings(): void {
243 }
244
245 /**
246 * Provides data for the frontend.
247 *
248 * @return array An array of data for the frontend.
249 */
250 public function data_frontend(): array {
251 return array();
252 }
253 }
254