PluginProbe ʕ •ᴥ•ʔ
Admin and Site Enhancements (ASE) / 6.9.2
Admin and Site Enhancements (ASE) v6.9.2
9.1.0 9.0.2 9.0.1 9.0.0 8.9.2 8.9.1 8.9.0 8.8.8 8.8.7 8.8.6 8.8.5 8.8.4 8.8.3 8.8.2 8.8.1 8.8.0 8.7.3 8.7.2 8.7.1 8.2.1 8.2.2 8.2.3 8.3.0 8.3.1 8.3.2 8.4.0 8.4.1 8.4.2 8.5.0 8.5.1 8.5.2 8.6.0 8.6.1 8.6.2 8.7.0 5.0.1 5.0.2 5.0.2.1 5.0.2.2 5.0.2.3 5.0.2.4 5.1.0 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.8.0 5.8.1 6.0.0 6.0.3 6.0.4 6.0.5 6.0.5.1 6.0.6 6.0.7 6.0.8.1 6.1.0 6.1.3 6.2.0 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.3.0 6.3.1 6.3.2 6.4.0 6.5.0 6.5.1 6.6.0 6.7.0 6.8.0 6.8.2 6.8.3 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13.1 6.9.13.2 6.9.2 6.9.3 6.9.4 6.9.5 6.9.6 6.9.7 6.9.8 6.9.9 7.0.0 7.0.2 7.0.2.1 7.0.2.2 7.0.2.3 7.0.3 7.1.0 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2.0 7.2.1 7.3.0 7.3.1 7.3.2 7.3.3 7.4.0 7.4.2 7.4.4 7.4.5 7.4.6 7.4.7 7.4.8 7.5.0 7.5.1 7.5.2 7.5.3 7.5.4 7.6.0 7.6.1 7.6.1.1 7.6.10 7.6.11 7.6.2 7.6.3 7.6.4 7.6.5 7.6.6 7.6.7 7.6.7.1 7.6.8 7.6.9 7.7.0 7.8.0 7.8.1 7.8.10 7.8.11 7.8.12 7.8.13 7.8.14 7.8.15 7.8.16 7.8.17 7.8.18 7.8.2 7.8.3 7.8.4 7.8.5 7.8.5.1 7.8.6 7.8.7 7.8.8 7.8.9 7.9.0 7.9.1 7.9.10 7.9.11 7.9.2 7.9.3 7.9.4 7.9.5 7.9.6 7.9.7 7.9.8 7.9.9 8.0.0 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.0.6 8.0.7 8.0.8 8.1.0 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.7.0 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.2.0 3.3.0 3.4.0 3.5.0 3.6.1 3.7.0 3.8.0 3.9.0 3.9.1 3.9.2 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.9.0 4.9.1 4.9.2 4.9.3 5.0.0
admin-site-enhancements / classes / class-security.php
admin-site-enhancements / classes Last commit date
class-activation.php 2 years ago class-admin-interface.php 2 years ago class-common-methods.php 2 years ago class-content-management.php 2 years ago class-custom-code.php 2 years ago class-deactivation.php 2 years ago class-disable-components.php 2 years ago class-login-logout.php 2 years ago class-optimizations.php 2 years ago class-security.php 2 years ago class-settings-fields-render.php 2 years ago class-settings-sanitization.php 2 years ago class-settings-sections-fields.php 2 years ago class-utilities.php 2 years ago
class-security.php
605 lines
1 <?php
2
3 namespace ASENHA\Classes;
4
5 use WP_Error ;
6 /**
7 * Class related to Security features
8 *
9 * @since 1.4.0
10 */
11 class Security
12 {
13 /**
14 * Maybe allow login if not locked out. Should return WP_Error object if not allowed to login.
15 *
16 * @since 2.5.0
17 */
18 public function maybe_allow_login( $user_or_error, $username, $password )
19 {
20 global $wpdb, $asenha_limit_login ;
21 $table_name = $wpdb->prefix . 'asenha_failed_logins';
22 // Maybe create table if it does not exist yet, e.g. upgraded from previous version of plugin, so, no activation methods are fired
23 $query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table_name ) );
24
25 if ( $wpdb->get_var( $query ) === $table_name ) {
26 // Table already exists, do nothing.
27 } else {
28 $activation = new Activation();
29 $activation->create_failed_logins_log_table();
30 }
31
32 // Get values from options needed to do various checks
33 $options = get_option( ASENHA_SLUG_U, array() );
34 $login_fails_allowed = $options['login_fails_allowed'];
35 $login_lockout_maxcount = $options['login_lockout_maxcount'];
36 $ip_address_whitelist_raw = ( isset( $options['limit_login_attempts_ip_whitelist'] ) ? explode( PHP_EOL, $options['limit_login_attempts_ip_whitelist'] ) : array() );
37 $ip_address_whitelist = array();
38 if ( !empty($ip_address_whitelist_raw) ) {
39 foreach ( $ip_address_whitelist_raw as $ip_address ) {
40 $ip_address_whitelist[] = trim( $ip_address );
41 }
42 }
43 $change_login_url = $options['change_login_url'];
44 $custom_login_slug = $options['custom_login_slug'];
45 // Instantiate object to access common methods
46 $common_methods = new Common_Methods();
47 // Get user/visitor IP address
48 $ip_address = $common_methods->get_user_ip_address();
49
50 if ( !in_array( $ip_address, $ip_address_whitelist ) ) {
51 // IP is not whitelisted
52 // Check if IP address has failed login attempts recorded in the DB log
53 $sql = $wpdb->prepare( "SELECT * FROM `" . $table_name . "` Where `ip_address` = %s", $ip_address );
54 $result = $wpdb->get_results( $sql, ARRAY_A );
55 $result_count = count( $result );
56
57 if ( $result_count > 0 ) {
58 // IP address has been recorded in the database.
59 $fail_count = $result[0]['fail_count'];
60 $lockout_count = $result[0]['lockout_count'];
61 $last_fail_on = $result[0]['unixtime'];
62 } else {
63 $fail_count = 0;
64 $lockout_count = 0;
65 $last_fail_on = '';
66 }
67
68 } else {
69 // IP is whitelisted
70 $result = array();
71 $result_count = 0;
72 $fail_count = 0;
73 $lockout_count = 0;
74 $last_fail_on = '';
75 }
76
77 // Initialize the global variable
78 $asenha_limit_login = array(
79 'ip_address' => $ip_address,
80 'request_uri' => sanitize_text_field( $_SERVER['REQUEST_URI'] ),
81 'ip_address_log' => $result,
82 'fail_count' => $fail_count,
83 'lockout_count' => $lockout_count,
84 'maybe_lockout' => false,
85 'extended_lockout' => false,
86 'within_lockout_period' => false,
87 'lockout_period' => 0,
88 'lockout_period_remaining' => 0,
89 'login_fails_allowed' => $login_fails_allowed,
90 'login_lockout_maxcount' => $login_lockout_maxcount,
91 'default_lockout_period' => 60 * 15,
92 'extended_lockout_period' => 24 * 60 * 60,
93 'change_login_url' => $change_login_url,
94 'custom_login_slug' => $custom_login_slug,
95 );
96
97 if ( !in_array( $ip_address, $ip_address_whitelist ) ) {
98 // IP is not whitelisted
99
100 if ( $result_count > 0 ) {
101 // IP address has been recorded in the database.
102 // Failed attempts have been recorded and fulfills lockout condition
103
104 if ( !empty($fail_count) && $fail_count % $login_fails_allowed == 0 ) {
105 $asenha_limit_login['maybe_lockout'] = true;
106 // Has reached max / gone beyond number of lockouts allowed?
107
108 if ( $lockout_count >= $login_lockout_maxcount ) {
109 $asenha_limit_login['extended_lockout'] = true;
110 $lockout_period = $asenha_limit_login['extended_lockout_period'];
111 } else {
112 $asenha_limit_login['extended_lockout'] = false;
113 $lockout_period = $asenha_limit_login['default_lockout_period'];
114 }
115
116 $asenha_limit_login['lockout_period'] = $lockout_period;
117 // User/visitor is still within the lockout period
118
119 if ( time() - $last_fail_on <= $asenha_limit_login['lockout_period'] ) {
120 $asenha_limit_login['within_lockout_period'] = true;
121 $asenha_limit_login['lockout_period_remaining'] = $asenha_limit_login['lockout_period'] - (time() - $last_fail_on);
122
123 if ( $asenha_limit_login['lockout_period_remaining'] <= 60 ) {
124 // Get remaining lockout period in minutes and seconds
125 $lockout_period_remaining = $asenha_limit_login['lockout_period_remaining'] . ' seconds';
126 } elseif ( $asenha_limit_login['lockout_period_remaining'] <= 60 * 60 ) {
127 // Get remaining lockout period in minutes and seconds
128 $lockout_period_remaining = $common_methods->seconds_to_period( $asenha_limit_login['lockout_period_remaining'], 'to-minutes-seconds' );
129 } elseif ( $asenha_limit_login['lockout_period_remaining'] > 60 * 60 && $asenha_limit_login['lockout_period_remaining'] <= 24 * 60 * 60 ) {
130 // Get remaining lockout period in minutes and seconds
131 $lockout_period_remaining = $common_methods->seconds_to_period( $asenha_limit_login['lockout_period_remaining'], 'to-hours-minutes-seconds' );
132 } elseif ( $asenha_limit_login['lockout_period_remaining'] > 24 * 60 * 60 ) {
133 // Get remaining lockout period in minutes and seconds
134 $lockout_period_remaining = $common_methods->seconds_to_period( $asenha_limit_login['lockout_period_remaining'], 'to-days-hours-minutes-seconds' );
135 }
136
137 $error = new WP_Error( 'ip_address_blocked', '<b>WARNING:</b> You\'ve been locked out. You can login again in ' . $lockout_period_remaining . '.' );
138 return $error;
139 } else {
140 // User/visitor is no longer within the lockout period
141 $asenha_limit_login['within_lockout_period'] = false;
142
143 if ( $lockout_count == $login_lockout_maxcount ) {
144 // Remove the DB log entry for the current IP address. i.e. release from extended lockout
145 $where = array(
146 'ip_address' => $ip_address,
147 );
148 $where_format = array( '%s' );
149 // Delete existing data in the database
150 $wpdb->delete( $table_name, $where, $where_format );
151 }
152
153 return $user_or_error;
154 }
155
156 } else {
157 $asenha_limit_login['maybe_lockout'] = false;
158 return $user_or_error;
159 }
160
161 } else {
162 // IP address has not been recorded in the database.
163 return $user_or_error;
164 }
165
166 } else {
167 // IP is whitelisted
168 return $user_or_error;
169 }
170
171 }
172
173 /**
174 * Disable login form inputs via CSS
175 *
176 * @since 2.5.0
177 */
178 public function maybe_hide_login_form()
179 {
180 global $asenha_limit_login ;
181
182 if ( isset( $asenha_limit_login['within_lockout_period'] ) && $asenha_limit_login['within_lockout_period'] ) {
183 // Hide logo, login form and the links below it
184 ?>
185 <script>
186 document.addEventListener("DOMContentLoaded", function(event) {
187 var loginForm = document.getElementById("loginform");
188 loginForm.remove();
189 });
190 </script>
191 <style type="text/css">
192
193 body.login {
194 background:#f6d6d7;
195 }
196
197 #login h1,
198 #loginform,
199 #login #nav,
200 #backtoblog,
201 .language-switcher {
202 display: none;
203 }
204
205 @media screen and (max-height: 550px) {
206
207 #login {
208 padding: 80px 0 20px !important;
209 }
210
211 }
212
213 </style>
214 <?php
215 } else {
216 $options = get_option( ASENHA_SLUG_U, array() );
217 $login_fails_allowed = $options['login_fails_allowed'];
218 $page_was_reloaded = ( isset( $_GET['rl'] ) && 1 == sanitize_text_field( $_GET['rl'] ) ? true : false );
219 if ( isset( $asenha_limit_login['fail_count'] ) && ($login_fails_allowed - 1 == intval( $asenha_limit_login['fail_count'] ) || 2 * $login_fails_allowed - 1 == intval( $asenha_limit_login['fail_count'] ) || 3 * $login_fails_allowed - 1 == intval( $asenha_limit_login['fail_count'] ) || 4 * $login_fails_allowed - 1 == intval( $asenha_limit_login['fail_count'] ) || 5 * $login_fails_allowed - 1 == intval( $asenha_limit_login['fail_count'] ) || 6 * $login_fails_allowed - 1 == intval( $asenha_limit_login['fail_count'] )) ) {
220
221 if ( array_key_exists( 'change_login_url', $options ) && $options['change_login_url'] ) {
222 // Custom Login URL is enabled, e.g. /manage
223 // Do nothing
224 } else {
225 // Default login URL, i.e. /wp-login.php
226 // Reload the login page so we get the up-to-date data in $asenha_limit_login
227 // Only reload if page was not reloaded before. This prevents infinite reloads.
228 if ( !$page_was_reloaded ) {
229 ?>
230 <script>
231 let url = window.location.href;
232 if (url.indexOf('?') > -1){
233 url += '&rl=1'
234 } else {
235 url += '?rl=1'
236 }
237 location.replace(url);
238 </script>
239 <?php
240 }
241 }
242
243 }
244 }
245
246 }
247
248 /**
249 * Log failed login attempts
250 *
251 * @since 2.5.0
252 */
253 public function log_failed_login( $username )
254 {
255 global $wpdb, $asenha_limit_login ;
256 $table_name = $wpdb->prefix . 'asenha_failed_logins';
257 // Check if the IP address has been used in a failed login attempt before, i.e. has it been recorded in the database?
258 $sql = $wpdb->prepare( "SELECT * FROM `" . $table_name . "` WHERE `ip_address` = %s", $asenha_limit_login['ip_address'] );
259 $result = $wpdb->get_results( $sql, ARRAY_A );
260 $result_count = count( $result );
261 // Update logged info for the IP address in the global variable
262 $asenha_limit_login['ip_address_log'] = $result;
263
264 if ( $result_count == 0 ) {
265 // IP address has not been recorded in the database.
266 $new_fail_count = 1;
267 $new_lockout_count = 0;
268 } else {
269 // IP address has been recorded in the database.
270 $new_fail_count = $result[0]['fail_count'] + 1;
271 $new_lockout_count = floor( ($result[0]['fail_count'] + 1) / $asenha_limit_login['login_fails_allowed'] );
272 }
273
274 // Get the URL where login failed, i.e. where brute force attack might be happening
275 // $login_url = ( ! empty( $_SERVER['HTTPS'] ) ? 'https://' : 'http://') . sanitize_text_field( $_SERVER['HTTP_HOST'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] );
276 // Time stamps
277 $unixtime = time();
278
279 if ( function_exists( 'wp_date' ) ) {
280 $datetime_wp = wp_date( 'Y-m-d H:i:s', $unixtime );
281 } else {
282 $datetime_wp = date_i18n( 'Y-m-d H:i:s', $unixtime );
283 }
284
285 $data = array(
286 'ip_address' => $asenha_limit_login['ip_address'],
287 'username' => $username,
288 'fail_count' => $new_fail_count,
289 'lockout_count' => $new_lockout_count,
290 'request_uri' => $asenha_limit_login['request_uri'],
291 'unixtime' => $unixtime,
292 'datetime_wp' => $datetime_wp,
293 'info' => '',
294 );
295 $data_format = array(
296 '%s',
297 // string
298 '%s',
299 // string
300 '%d',
301 // integer
302 '%d',
303 // integer
304 '%s',
305 // string
306 '%d',
307 // integer
308 '%s',
309 // string
310 '%s',
311 );
312
313 if ( $result_count == 0 ) {
314 // Insert into the database
315 $result = $wpdb->insert( $table_name, $data, $data_format );
316 } else {
317 // $options = get_option( ASENHA_SLUG_U );
318 // $login_fails_allowed = $options['login_fails_allowed'];
319 $fail_count = $result[0]['fail_count'];
320 $lockout_count = $result[0]['lockout_count'];
321 $last_fail_on = $result[0]['unixtime'];
322 $where = array(
323 'ip_address' => $asenha_limit_login['ip_address'],
324 );
325 $where_format = array( '%s' );
326 // Failed attempts have been recorded and fulfills lockout condition
327
328 if ( !empty($fail_count) && $fail_count % $asenha_limit_login['login_fails_allowed'] == 0 ) {
329 // Has reached max / gone beyond number of lockouts allowed?
330
331 if ( $lockout_count >= $asenha_limit_login['login_lockout_maxcount'] ) {
332 $asenha_limit_login['extended_lockout'] = true;
333 $lockout_period = $asenha_limit_login['extended_lockout_period'];
334 } else {
335 $asenha_limit_login['extended_lockout'] = false;
336 $lockout_period = $asenha_limit_login['default_lockout_period'];
337 }
338
339 $asenha_limit_login['lockout_period'] = $lockout_period;
340 // User/visitor is still within the lockout period
341
342 if ( time() - $last_fail_on <= $asenha_limit_login['lockout_period'] ) {
343 // Do nothing
344 } else {
345 if ( $lockout_count < $asenha_limit_login['login_lockout_maxcount'] ) {
346 // Update existing data in the database
347 $wpdb->update(
348 $table_name,
349 $data,
350 $where,
351 $data_format,
352 $where_format
353 );
354 }
355 }
356
357 } else {
358 // Update existing data in the database
359 $wpdb->update(
360 $table_name,
361 $data,
362 $where,
363 $data_format,
364 $where_format
365 );
366 }
367
368 }
369
370 }
371
372 /**
373 * Handle login errors
374 *
375 * @link https://developer.wordpress.org/reference/classes/wp_error/#methods
376 * @since 2.5.0
377 */
378 public function login_error_handler( $errors, $redirect_to )
379 {
380 global $asenha_limit_login ;
381
382 if ( is_wp_error( $errors ) ) {
383 $error_codes = $errors->get_error_codes();
384 foreach ( $error_codes as $error_code ) {
385
386 if ( $error_code == 'invalid_username' || $error_code == 'incorrect_password' ) {
387 // Remove default error messages that may give out valueable info to hackers
388 $errors->remove( 'invalid_username' );
389 // Outputs info that says username does not exist. May encourage login attempt with a different username instead.
390 $errors->remove( 'incorrect_password' );
391 // Outputs info that implies username exist. May encourage login attempt with a different password.
392 // Add a new error message that does not provide useful clues to hackers
393 $errors->add( 'invalid_username_or_incorrect_password', '<b>Error:</b> Invalid username/email or incorrect password.' );
394 // $errors->add( 'another_error_code', 'The error message.' );
395 }
396
397 }
398 }
399
400 return $errors;
401 }
402
403 /**
404 * Add login error message on top of the login form
405 *
406 * @since 2.5.0
407 */
408 public function add_failed_login_message( $message )
409 {
410 global $asenha_limit_login ;
411 if ( isset( $_REQUEST['failed_login'] ) && $_REQUEST['failed_login'] == 'true' ) {
412 if ( !is_null( $asenha_limit_login ) && isset( $asenha_limit_login['within_lockout_period'] ) && !$asenha_limit_login['within_lockout_period'] ) {
413 $message = '<div id="login_error" class="notice notice-error"><b>Error:</b> Invalid username/email or incorrect password.</div>';
414 }
415 }
416 return $message;
417 }
418
419 /**
420 * Clear failed login attempts log after successful login
421 *
422 * @since 2.5.0
423 */
424 public function clear_failed_login_log()
425 {
426 global $wpdb, $asenha_limit_login ;
427 $table_name = $wpdb->prefix . 'asenha_failed_logins';
428 $ip_address = ( isset( $asenha_limit_login['ip_address'] ) ? $asenha_limit_login['ip_address'] : '' );
429 // Remove the DB log entry for the current IP address.
430 $where = array(
431 'ip_address' => $ip_address,
432 );
433 $where_format = array( '%s' );
434 $wpdb->delete( $table_name, $where, $where_format );
435 }
436
437 /**
438 * Obfuscate email address on the frontend using antispambot() native WP function
439 *
440 * @link: https://gist.github.com/eclarrrk/349360b52e8822b69cb6fc499722520f
441 * @since 5.5.0
442 */
443 public function obfuscate_string( $atts )
444 {
445 $atts = shortcode_atts( array(
446 'email' => '',
447 'subject' => '',
448 'display' => 'newline',
449 'link' => 'no',
450 'class' => '',
451 ), $atts );
452 $email = $atts['email'];
453 if ( !is_email( $email ) ) {
454 return;
455 }
456 // Reverse email address characters if not in Firefox, which has bug related to unicode-bidi CSS property
457 $http_user_agent = ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : 'generic' );
458
459 if ( false !== stripos( sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ), 'firefox' ) ) {
460 // Do nothing. Do not reverse characters.
461 $email_reversed = $email;
462 $email_rev_parts = explode( '@', $email_reversed );
463 $email_rev_parts = array( $email_rev_parts[0], $email_rev_parts[1] );
464 $css_bidi_styles = '';
465 } else {
466 $email_reversed = strrev( $email );
467 $email_rev_parts = explode( '@', $email_reversed );
468 $css_bidi_styles = 'unicode-bidi:bidi-override;';
469 }
470
471 $display = $atts['display'];
472
473 if ( 'newline' == $display ) {
474 $display_css = 'display:flex;justify-content:flex-end;';
475 } elseif ( 'inline' == $display ) {
476 $display_css = 'display:inline;';
477 }
478
479 $subject = $atts['subject'];
480 if ( !empty($subject) ) {
481 $subject = '?subject=' . $subject;
482 }
483 $link = $atts['link'];
484 $class = $atts['class'];
485 return '<div style="display:inline;' . esc_attr( $css_bidi_styles ) . ';direction:rtl;" class="' . esc_attr( $class ) . '">' . esc_html( $email_rev_parts[0] ) . '<span style="display:none;">obfsctd</span>&#64;' . esc_html( $email_rev_parts[1] ) . '</div>';
486 }
487
488 /**
489 * If an author name is queried, decrypt it. Used by pre_get_posts action.
490 *
491 * @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/tags/4.0.2/inc/class-smart-user-slug-hider.php
492 * @since 2.1.0
493 */
494 function alter_author_query( $query )
495 {
496 // Check if it's a query for author data, and that 'author_name' is not empty
497 if ( $query->is_author() && $query->query_vars['author_name'] != '' ) {
498 // Check for character(s) representing a hexadecimal digit
499
500 if ( ctype_xdigit( $query->query_vars['author_name'] ) ) {
501 // Get user by the decrypted user ID
502 $user = get_user_by( 'id', $this->decrypt( $query->query_vars['author_name'] ) );
503
504 if ( $user ) {
505 $query->set( 'author_name', $user->user_nicename );
506 } else {
507 // No user found
508 $query->is_404 = true;
509 $query->is_author = false;
510 $query->is_archive = false;
511 }
512
513 } else {
514 // No hexadecimal digit detected in URL, i.e. someone is trying to access URL with original author slug
515 $query->is_404 = true;
516 $query->is_author = false;
517 $query->is_archive = false;
518 }
519
520 }
521 return;
522 }
523
524 /**
525 * Replace author slug in author link to encrypted value. Used by author_link filter.
526 *
527 * @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/tags/4.0.2/inc/class-smart-user-slug-hider.php
528 * @since 2.1.0
529 */
530 function alter_author_link( $link, $user_id, $author_slug )
531 {
532 $encrypted_author_slug = $this->encrypt( $user_id );
533 return str_replace( '/' . $author_slug, '/' . $encrypted_author_slug, $link );
534 }
535
536 /**
537 * Replace author slug in REST API /users/ endpoint to encrypted value. Used by rest_prepare_user filter.
538 *
539 * @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/tags/4.0.2/inc/class-smart-user-slug-hider.php
540 * @since 2.1.0
541 */
542 function alter_json_users( $response, $user, $request )
543 {
544 $data = $response->get_data();
545 $data['slug'] = $this->encrypt( $data['id'] );
546 $response->set_data( $data );
547 return $response;
548 }
549
550 /**
551 * Helper function to return an encrypted user ID, which will then be used to replace the author slug.
552 *
553 * @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/trunk/inc/class-smart-user-slug-hider.php
554 * @since 2.1.0
555 */
556 private function encrypt( $user_id )
557 {
558 // Returns encrypted encrypted author slug from user ID, e.g. encrypt user ID 3 to author slug 4e3062d8c8626a14
559 return bin2hex( openssl_encrypt(
560 base_convert( $user_id, 10, 36 ),
561 'DES-EDE3',
562 md5( ASENHA_URL ),
563 OPENSSL_RAW_DATA
564 ) );
565 }
566
567 /**
568 * Helper function to decrypt an (encrypted) author slug and returns the user ID
569 *
570 * @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/trunk/inc/class-smart-user-slug-hider.php
571 * @since 2.1.0
572 */
573 private function decrypt( $encrypted_author_slug )
574 {
575 // Returns user ID, e.g. decrypts author slug 4e3062d8c8626a14 into user ID 3
576 return base_convert( openssl_decrypt(
577 pack( 'H*', $encrypted_author_slug ),
578 'DES-EDE3',
579 md5( ASENHA_URL ),
580 OPENSSL_RAW_DATA
581 ), 36, 10 );
582 }
583
584 /**
585 * Remove XML RPC link in head
586 *
587 * @since 6.2.2
588 */
589 public function remove_xmlrpc_link()
590 {
591 remove_action( 'wp_head', 'rsd_link' );
592 }
593
594 /**
595 * Disable the XML-RPC component
596 *
597 * @since 2.2.0
598 */
599 public function maybe_disable_xmlrpc( $data )
600 {
601 http_response_code( 403 );
602 exit( 'You don\'t have permission to access this file.' );
603 }
604
605 }