PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.4.1
Defender Security – Malware Scanner, Login Security & Firewall v5.4.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.4.1, at src/controller/class-bot-trap.php

253 lines 6.3 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( 'template_redirect', array( $this, 'handle_hash_url' ) );
53 }
54 }
55 }
56
57 /**
58 * Initializes the bot trap functionality.
59 * Schedules a weekly cron job to rotate the hash and registers a rewrite rule.
60 */
61 public function init() {
62 $this->schedule_cron();
63
64 if ( ! $this->service->get_hash() ) {
65 $this->service->rotate_hash();
66 }
67
68 $this->flush_rewrite();
69 $this->service->handle_robots_txt();
70 }
71
72 /**
73 * Schedules a weekly cron job to rotate the bot trap hash.
74 * This ensures that the bot trap URL changes weekly.
75 */
76 public function schedule_cron() {
77 if ( ! wp_next_scheduled( 'wpdef_rotate_bot_trap_secret_hash' ) ) {
78 wp_schedule_event( time(), 'weekly', 'wpdef_rotate_bot_trap_secret_hash' );
79 }
80 }
81
82 /**
83 * Registers a rewrite rule for the bot trap URL.
84 * The URL will be in the format: /{hash}/
85 * where {hash} is a 16-character hexadecimal string.
86 */
87 public function register_rewrite_rule() {
88 $hash = $this->service->get_hash();
89 add_rewrite_rule( "^{$hash}/?$", 'index.php?' . self::URL_QUERY . '=' . $hash, 'top' );
90 }
91
92 /**
93 * Rotates the bot trap hash and flushes rewrite rules.
94 */
95 public function rotate_hash() {
96 $this->service->rotate_hash();
97 }
98
99 /**
100 * Adds a query variable for the bot trap URL.
101 * This allows us to capture the hash from the URL.
102 *
103 * @param array $vars Existing query variables.
104 * @return array Modified query variables.
105 */
106 public function add_query_var( $vars ) {
107 $vars[] = self::URL_QUERY;
108 return $vars;
109 }
110
111 /**
112 * Handles the bot trap URL when accessed.
113 * If the hash in the URL matches the stored hash, block the IP.
114 * Otherwise, it will do nothing.
115 */
116 public function handle_hash_url() {
117 $used_hash = get_query_var( self::URL_QUERY );
118 $valid_hash = $this->service->get_hash();
119
120 if ( $used_hash === $valid_hash ) {
121 $known_bots = Known_Bots_Factory::create();
122 $bot_ips = $known_bots->get_all_bot_ips();
123
124 // Flatten 2D array into a single array.
125 $flattened_bot_ips = array();
126 foreach ( $bot_ips as $ips ) {
127 foreach ( $ips as $ip ) {
128 $flattened_bot_ips[] = $ip;
129 }
130 }
131
132 $model = $this->service->model;
133 $ips = $this->service->get_user_ip();
134
135 foreach ( $ips as $ip ) {
136 // Skip if the IP is a known bot IP.
137 if ( $this->is_ip_in_format( $ip, $flattened_bot_ips ) ) {
138 continue;
139 }
140
141 $lockout_model = Lockout_Ip::get( $ip );
142 $remaining_time = 0;
143 if ( 'permanent' === $model->bot_trap_lockout_type ) {
144 $lockout_model->attempt = 0;
145 $lockout_model->meta['login'] = array();
146 $lockout_model->meta['nf'] = array();
147 $lockout_model->save();
148
149 do_action( 'wd_blacklist_this_ip', $ip );
150 } else {
151 $lockout_model->status = Lockout_Ip::STATUS_BLOCKED;
152 $lockout_model->lock_time = time();
153
154 $this->service->create_blocked_lockout(
155 $lockout_model,
156 $model->bot_trap_message,
157 strtotime( '+' . $model->bot_trap_lockout_duration . ' ' . $model->bot_trap_lockout_duration_unit )
158 );
159
160 $remaining_time = $lockout_model->remaining_release_time();
161 }
162
163 // Need to create a log.
164 $this->service->log_event( $ip, $used_hash, Bot_Trap_Component::SCENARIO_BOT_TRAP );
165
166 wd_di()->get( Firewall::class )->actions_for_blocked(
167 $model->bot_trap_message,
168 $remaining_time,
169 Bot_Trap_Component::SCENARIO_BOT_TRAP,
170 $ips
171 );
172 }
173 }
174 }
175
176 /**
177 * Injects the bot trap URL into the footer of frontend pages.
178 * This URL is hidden.
179 */
180 public function inject_footer() {
181 if ( is_admin() ) {
182 return;
183 }
184
185 $hash = $this->service->get_hash();
186 echo '<div style="display:none;"><a href="' . esc_url( home_url( "/{$hash}" ) ) . '">Secret Link</a></div>';
187 }
188
189 /**
190 * Flushes the rewrite rules to ensure the new bot trap URL is recognized.
191 */
192 public function flush_rewrite() {
193 $this->register_rewrite_rule();
194 flush_rewrite_rules();
195 }
196
197 /**
198 * Delete all the data & the cache.
199 */
200 public function remove_data() {
201 // Remove the bot trap hash from options.
202 delete_site_option( Bot_Trap_Component::URL_HASH_KEY );
203
204 $this->service->remove_rule();
205
206 // Flush rewrite rules to remove the bot trap URL.
207 flush_rewrite_rules();
208 }
209
210 /**
211 * Exports strings.
212 *
213 * @return array An array of strings.
214 */
215 public function export_strings(): array {
216 return array();
217 }
218
219 /**
220 * Converts the object data to an array.
221 *
222 * @return array An array representation of the object.
223 */
224 public function to_array(): array {
225 return array();
226 }
227
228 /**
229 * Imports data into the model.
230 *
231 * @param array $data Data to be imported into the model.
232 *
233 * @throws Exception If table is not defined.
234 */
235 public function import_data( array $data ) {
236 }
237
238 /**
239 * Removes settings for all submodules.
240 */
241 public function remove_settings(): void {
242 }
243
244 /**
245 * Provides data for the frontend.
246 *
247 * @return array An array of data for the frontend.
248 */
249 public function data_frontend(): array {
250 return array();
251 }
252 }
253