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/controller/class-mask-login.php +123 -64 5.3.1 → 6.3.0 View file →
@@ -57,9 +57,8 @@
57 57 /**
58 58 * Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
59 59 */
60 60 public function __construct() {
61 - add_filter( 'wp_defender_advanced_tools_data', array( $this, 'script_data' ) );
62 61 // Internal cache, so we don't need to query many times.
63 62 $this->model = wd_di()->get( Model_Mask_Login::class );
64 63 $this->service = wd_di()->get( Component_Mask_Login::class );
65 64 $this->register_routes();
@@ -158,8 +157,9 @@
158 157 * @return void
159 158 */
160 159 public function show_login_page(): void {
161 160 global $error, $interim_login, $action, $user_login, $user, $redirect_to;
161 + // Simulate the environment as a "login page".
162 162 $GLOBALS['pagenow'] = 'wp-login.php'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
163 163 if ( $this->service->is_recovery_mode() ) {
164 164 ( new WP_Recovery_Mode() )->initialize();
165 165 }
@@ -173,14 +173,10 @@
173 173 * @return void
174 174 * @since 2.8.0
175 175 */
176 176 public function before_mask_login_handle(): void {
177 - // Some plugins for Cron actions clear HTTP_HOST-param.
178 - $host = defender_get_data_from_request( 'HTTP_HOST', 's' );
179 - if ( ! isset( $host['HTTP_HOST'] ) ) {
180 - $host = '';
181 - }
182 - $current_url = set_url_scheme( 'http://' . $host . defender_get_data_from_request( 'REQUEST_URI', 's' ) );
177 + $request_uri = (string) defender_get_data_from_request( 'REQUEST_URI', 's' );
178 + $current_url = home_url( $request_uri );
183 179 $login_url = $this->get_model()->get_new_login_url( $this->get_site_url() );
184 180
185 181 if (
186 182 ! is_user_logged_in() &&
@@ -214,8 +210,11 @@
214 210 // Make sure that wp-login.php is never used in site URLs or redirects.
215 211 if ( ! $this->service->is_login_url() ) {
216 212 $redirect_path = trim( (string) wp_parse_url( $location, PHP_URL_PATH ), '/' );
217 213 if ( $redirect_path === $this->get_model()->mask_url ) {
214 + if ( $this->is_recipient_email_action() ) {
215 + return $location;
216 + }
218 217 $this->maybe_lock();
219 218 }
220 219 }
221 220
@@ -240,9 +239,9 @@
240 239 // Need to check if the current request is for signup, login.
241 240 // If it is not the slug, then we redirect to the 404 redirect, or 403 wp die.
242 241 $requested_path = $this->service->get_request_path();
243 242 $requested_path_without_slash = ltrim( $requested_path, '/' );
244 - if ( ! $requested_path_without_slash && ! empty( get_option( 'permalink_structure' ) ) ) {
243 + if ( ! $requested_path_without_slash && '' !== get_option( 'permalink_structure', '' ) ) {
245 244 return;
246 245 } else {
247 246 $params = wp_parse_args( defender_get_data_from_request( 'QUERY_STRING', 's' ), array() );
248 247 if ( isset( $params[ $this->model->mask_url ] ) ) {
@@ -251,9 +250,10 @@
251 250 }
252 251
253 252 if ( '/' . ltrim( $this->get_model()->mask_url, '/' ) === $requested_path ) {
254 253 // We need to redirect this one to wp-login and open it.
255 - return $this->show_login_page();
254 + $this->show_login_page();
255 + return;
256 256 }
257 257 /**
258 258 * Allowed if:
259 259 * it's AJAX,
@@ -291,9 +291,10 @@
291 291 }
292 292
293 293 // If current is same then we show the login screen.
294 294 if ( $this->service->is_land_on_masked_url( $this->model->mask_url ) ) {
295 - return $this->show_login_page();
295 + $this->show_login_page();
296 + return;
296 297 }
297 298
298 299 // If it's the verification link to change Network Admin Email.
299 300 $is_multisite = is_multisite();
@@ -319,9 +320,10 @@
319 320 ( ! $is_multisite && 'wp-signup.php' === $requested_path_without_slash )
320 321 || $this->service->is_on_login_page( $requested_path_without_slash )
321 322 ) {
322 323 // If they are here and the flow getting here, then just lock.
323 - return $this->maybe_lock();
324 + $this->maybe_lock();
325 + return;
324 326 }
325 327 }
326 328
327 329 /**
@@ -349,10 +351,10 @@
349 351 $this->data_frontend()
350 352 )
351 353 );
352 354 }
353 - $data_frontend = $this->data_frontend();
354 - $result['message'] = $this->model->get_formatted_errors();
355 + $data_frontend = $this->data_frontend();
356 + $result = array( 'message' => $this->model->get_formatted_errors() );
355 357 // Don't hide the error notice if the module is not activated.
356 358 if ( ! $data_frontend['is_active'] ) {
357 359 $result['auto_close'] = false;
358 360 }
@@ -450,9 +452,9 @@
450 452 $requested_path = $this->service->get_request_path( $current_url );
451 453 if ( '/wp-admin' === $requested_path ) {
452 454 $current_domain = defender_get_data_from_request( 'HTTP_HOST', 's' );
453 455 $sub_domain = wp_parse_url( $current_url, PHP_URL_HOST );
454 - if ( ! empty( $sub_domain ) && false === stripos( $sub_domain, $current_domain ) ) {
456 + if ( is_string( $sub_domain ) && '' !== trim( $sub_domain ) && false === stripos( $sub_domain, $current_domain ) ) {
455 457 return $this->get_model()->get_new_login_url( $sub_domain );
456 458 }
457 459 }
458 460 }
@@ -471,13 +473,8 @@
471 473 *
472 474 * @return void
473 475 */
474 476 public function maybe_lock(): void {
475 - $forbidden_message = esc_html__(
476 - 'This feature is forbidden temporarily for security reason. Try login again.',
477 - 'defender-security'
478 - );
479 -
480 477 if ( 'custom_url' === $this->get_model()->redirect_traffic && strlen( $this->get_model()->redirect_traffic_url ) ) {
481 478 if ( 'url' === $this->get_model()->is_url_or_slug() ) {
482 479 $redirect_url = wp_sanitize_redirect( $this->get_model()->redirect_traffic_url );
483 480 $lp = wp_parse_url( $redirect_url );
@@ -483,9 +480,9 @@
483 480 $lp = wp_parse_url( $redirect_url );
484 481
485 482 // Give up if malformed URL.
486 483 if ( false === $lp ) {
487 - wp_die( esc_html( $forbidden_message ) );
484 + $this->show_forbidden_screen();
488 485 }
489 486 // If the URL is without scheme, e.g. example.com, then add 'http' protocol at the beginning of the URL.
490 487 if ( ! isset( $lp['scheme'] ) && isset( $lp['path'] ) ) {
491 488 $redirect_url = 'http://' . untrailingslashit( $redirect_url );
@@ -527,12 +524,25 @@
527 524
528 525 // Handle user profile email change request.
529 526 $this->handle_email_change_request();
530 527
531 - wp_die( esc_html( $forbidden_message ) );
528 + $this->show_forbidden_screen();
532 529 }
533 530
534 531 /**
532 + * Show the forbidden screen.
533 + */
534 + public function show_forbidden_screen(): void {
535 + wp_die(
536 + esc_html__( 'This feature is temporarily forbidden for security reasons. Try logging in again.', 'defender-security' ),
537 + esc_html__( 'Forbidden', 'defender-security' ),
538 + array(
539 + 'response' => 403,
540 + )
541 + );
542 + }
543 +
544 + /**
535 545 * Safe way to get cached model.
536 546 *
537 547 * @return Model_Mask_Login
538 548 */
@@ -544,21 +554,8 @@
544 554 return new Model_Mask_Login();
545 555 }
546 556
547 557 /**
548 - * Provide data to the frontend via localized script.
549 - *
550 - * @param array $data Data collection is ready to passed.
551 - *
552 - * @return array Modified data array with added this controller data.
553 - */
554 - public function script_data( array $data ): array {
555 - $data['mask_login'] = $this->data_frontend();
556 -
557 - return $data;
558 - }
559 -
560 - /**
561 558 * Redirects the user to the admin URL if the given URL is the home URL.
562 559 *
563 560 * @param string $url The URL to be redirected.
564 561 * @param string $raw_url The raw URL.
@@ -586,9 +583,9 @@
586 583 *
587 584 * @return string The site URL with the specified parameters.
588 585 */
589 586 private function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
590 - if ( empty( $blog_id ) || ! is_multisite() ) {
587 + if ( ! is_int( $blog_id ) || 0 === $blog_id || ! is_multisite() ) {
591 588 $url = get_option( 'siteurl' );
592 589 } else {
593 590 switch_to_blog( $blog_id );
594 591 $url = get_option( 'siteurl' );
@@ -848,24 +845,24 @@
848 845 * @return array An array of strings.
849 846 */
850 847 public function export_strings(): array {
851 848 return array(
852 - $this->get_model()->is_active() ? esc_html__( 'Active', 'defender-security' ) : esc_html__( 'Inactive', 'defender-security' ),
849 + \WP_Defender\Model\Setting\Mask_Login::get_module_name() . ' '
850 + . ( $this->get_model()->enabled ? esc_html__( 'active', 'defender-security' ) : esc_html__( 'inactive', 'defender-security' ) ),
853 851 );
854 852 }
855 853
856 854 /**
857 - * Generates configuration strings based on the provided configuration and
858 - * whether the product is a pro version.
855 + * Generates configuration strings based on the provided configuration.
859 856 *
860 857 * @param array $config Configuration data.
861 - * @param bool $is_pro Indicates if the product is a pro version.
862 858 *
863 859 * @return array Returns an array of configuration strings.
864 860 */
865 - public function config_strings( array $config, bool $is_pro ): array {
861 + public function config_strings( array $config ): array {
866 862 return array(
867 - $config['enabled'] ? esc_html__( 'Active', 'defender-security' ) : esc_html__( 'Inactive', 'defender-security' ),
863 + \WP_Defender\Model\Setting\Mask_Login::get_module_name() . ' '
864 + . ( $config['enabled'] ? esc_html__( 'active', 'defender-security' ) : esc_html__( 'inactive', 'defender-security' ) ),
868 865 );
869 866 }
870 867
871 868 /**
@@ -878,8 +875,12 @@
878 875 $action = defender_get_data_from_request( 'action', 'g' );
879 876 $key = wp_unslash( defender_get_data_from_request( 'key', 'r' ) );
880 877 $login = wp_unslash( defender_get_data_from_request( 'login', 'r' ) );
881 878 $wd_ml_token = defender_get_data_from_request( 'wd-ml-token', 'g' );
879 + $cookie_name = 'wd-rp-' . COOKIEHASH;
880 + $rp_path = defined( 'COOKIEPATH' ) ? COOKIEPATH : '/';
881 + $rp_domain = defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '';
882 +
882 883 if (
883 884 isset( $action, $key, $login, $wd_ml_token )
884 885 && 'rp' === $action
885 886 && $login === $wd_ml_token
@@ -886,35 +887,59 @@
886 887 ) {
887 888
888 889 $user = check_password_reset_key( $key, $login );
889 890 if ( ! is_wp_error( $user ) ) {
890 - $value = sprintf( '%s:%s', $login, $key );
891 - set_site_transient( 'wd-rp-' . COOKIEHASH, $value, 2 * MINUTE_IN_SECONDS );
891 + $token = wp_generate_password( 20, false );
892 + $transient_key = 'wd-rp-' . COOKIEHASH . '_' . $token;
893 + $value = sprintf( '%s:%s', $login, $key );
894 +
895 + set_site_transient( $transient_key, $value, 2 * MINUTE_IN_SECONDS );
896 + setcookie( $cookie_name, $token, 0, $rp_path, $rp_domain, is_ssl(), true );
897 + $_COOKIE[ $cookie_name ] = $token;
898 +
892 899 wp_safe_redirect( remove_query_arg( array( 'key', 'login', 'wd-ml-token' ) ) );
893 900 exit;
894 901 }
895 902 }
896 - $value = get_site_transient( 'wd-rp-' . COOKIEHASH );
903 +
904 + $cookie_token = defender_get_data_from_request( $cookie_name, 'c' );
905 + if ( ! is_string( $cookie_token ) || '' === trim( $cookie_token ) ) {
906 + return;
907 + }
908 +
909 + $transient_key = 'wd-rp-' . COOKIEHASH . '_' . $cookie_token;
910 + $value = get_site_transient( $transient_key );
911 +
897 912 // Process the data and display the result.
898 913 if (
899 914 isset( $action )
900 915 && in_array( $action, array( 'rp', 'resetpass' ), true )
901 - && isset( $value ) && 0 < strpos( $value, ':' )
916 + && is_string( $value ) && 0 < strpos( $value, ':' )
902 917 ) {
903 918 [ $login, $key ] = explode( ':', wp_unslash( $value ), 2 );
904 919 $user = check_password_reset_key( $key, $login );
920 +
921 + if ( is_wp_error( $user ) ) {
922 + delete_site_transient( $transient_key );
923 + setcookie( $cookie_name, ' ', time() - YEAR_IN_SECONDS, $rp_path, $rp_domain, is_ssl(), true );
924 + unset( $_COOKIE[ $cookie_name ] );
925 +
926 + return;
927 + }
928 +
905 929 if ( 'resetpass' === $action ) {
906 - delete_site_transient( 'wd-rp-' . COOKIEHASH );
930 + delete_site_transient( $transient_key );
931 + setcookie( $cookie_name, ' ', time() - YEAR_IN_SECONDS, $rp_path, $rp_domain, is_ssl(), true );
932 + unset( $_COOKIE[ $cookie_name ] );
907 933 }
908 - if ( ! is_wp_error( $user ) ) {
909 - $this->render_partial(
910 - 'mask-login/reset',
911 - array(
912 - 'user' => $user,
913 - )
914 - );
915 - exit;
916 - }
934 +
935 + $this->render_partial(
936 + 'mask-login/reset',
937 + array(
938 + 'user' => $user,
939 + )
940 + );
941 + exit;
917 942 }
918 943 }
919 944
920 945 /**
@@ -926,10 +951,15 @@
926 951 * @since 2.6.4
927 952 */
928 953 private function is_allowed_path( string $path ): bool {
929 954 // Admin post requests to admin-post.php should be allowed.
930 - $allowed = 'wp-admin/admin-post.php' === $path && ! empty( defender_get_data_from_request( 'action', 'r' ) );
955 + $action = defender_get_data_from_request( 'action', 'r' );
956 + $allowed = 'wp-admin/admin-post.php' === $path && is_string( $action ) && '' !== trim( $action );
931 957
958 + if ( 'wp-admin/admin-ajax.php' === $path && $this->is_recipient_email_action() ) {
959 + $allowed = true;
960 + }
961 +
932 962 /**
933 963 * Filter to allow whitelisting paths from login masking.
934 964 *
935 965 * @param bool $allowed Is current path allowed?.
@@ -940,8 +970,37 @@
940 970 return apply_filters( 'wd_mask_login_is_allowed_path', $allowed, $path );
941 971 }
942 972
943 973 /**
974 + * Check if the request is a signed recipient email action.
975 + *
976 + * @return bool
977 + */
978 + private function is_recipient_email_action(): bool {
979 + $action = defender_get_data_from_request( 'action', 'r' );
980 + $hash = defender_get_data_from_request( 'hash', 'r' );
981 + if ( ! is_string( $hash ) || '' === trim( $hash ) ) {
982 + return false;
983 + }
984 +
985 + if ( Notification::SLUG_UNSUBSCRIBE === $action ) {
986 + $slug = defender_get_data_from_request( 'slug', 'r' );
987 +
988 + return is_string( $slug ) && '' !== trim( $slug );
989 + }
990 +
991 + if ( Notification::SLUG_SUBSCRIBE !== $action ) {
992 + return false;
993 + }
994 +
995 + $slug = defender_get_data_from_request( 'uid', 'r' );
996 + $slugs = defender_get_data_from_request( 'uids', 'r' );
997 +
998 + return ( is_string( $slug ) && '' !== trim( $slug ) )
999 + || ( is_string( $slugs ) && '' !== trim( $slugs ) );
1000 + }
1001 +
1002 + /**
944 1003 * An endpoint for fetching Post/Page.
945 1004 *
946 1005 * @param Request $request Request data.
947 1006 *
@@ -1004,9 +1063,9 @@
1004 1063 public function posts_where_title( string $where, WP_Query $wp_query ) {
1005 1064 global $wpdb;
1006 1065
1007 1066 $search_term = $wp_query->get( 'search_by_post_title' );
1008 - if ( ! empty( $search_term ) ) {
1067 + if ( is_string( $search_term ) && '' !== trim( $search_term ) ) {
1009 1068 $where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . esc_sql( $wpdb->esc_like( $search_term ) ) . '%\'';
1010 1069 }
1011 1070
1012 1071 return $where;
@@ -1021,12 +1080,12 @@
1021 1080 *
1022 1081 * @return string The modified URL.
1023 1082 */
1024 1083 public function change_subsites_admin_url( string $url, string $path, $blog_id ) {
1025 - if ( empty( $path ) && ! empty( $blog_id ) ) {
1084 + if ( '' === trim( $path ) && is_int( $blog_id ) && 0 < $blog_id ) {
1026 1085 $mask_url = trim( $this->model->mask_url );
1027 1086
1028 - if ( ! empty( $mask_url ) && $this->check_if_domain_is_mapped( $url ) ) {
1087 + if ( '' !== $mask_url && $this->check_if_domain_is_mapped( $url ) ) {
1029 1088 $url = str_replace( 'wp-admin', $mask_url, untrailingslashit( $url ) );
1030 1089 }
1031 1090 }
1032 1091
@@ -1042,15 +1101,15 @@
1042 1101 */
1043 1102 public function check_if_domain_is_mapped( string $url ): bool {
1044 1103 $is_mapped = false;
1045 1104
1046 - if ( ! empty( $url ) ) {
1105 + if ( '' !== trim( $url ) ) {
1047 1106 $url_arr = wp_parse_url( $url );
1048 1107 $net_url_arr = wp_parse_url( network_site_url() );
1049 1108
1050 1109 if (
1051 - ! empty( $url_arr['host'] ) &&
1052 - ! empty( $net_url_arr['host'] ) &&
1110 + isset( $url_arr['host'] ) && is_string( $url_arr['host'] ) && '' !== trim( $url_arr['host'] ) &&
1111 + isset( $net_url_arr['host'] ) && is_string( $net_url_arr['host'] ) && '' !== trim( $net_url_arr['host'] ) &&
1053 1112 $this->get_domain_from_host( $url_arr['host'] ) !== $this->get_domain_from_host( $net_url_arr['host'] )
1054 1113 ) {
1055 1114 $is_mapped = true;
1056 1115 }
@@ -1090,9 +1149,9 @@
1090 1149 * @since 3.4.0
1091 1150 */
1092 1151 public function update_admin_bar_menu( WP_Admin_Bar $admin_bar ) {
1093 1152 $mask_url = trim( $this->model->mask_url );
1094 - if ( empty( $mask_url ) ) {
1153 + if ( '' === $mask_url ) {
1095 1154 return;
1096 1155 }
1097 1156
1098 1157 $admin_bar_nodes = $admin_bar->get_nodes();
@@ -1123,9 +1182,9 @@
1123 1182 */
1124 1183 public function update_myblogs_blog_actions( string $actions, object $user_blog ) {
1125 1184 $mask_url = trim( $this->model->mask_url );
1126 1185
1127 - if ( empty( $mask_url ) ) {
1186 + if ( '' === $mask_url ) {
1128 1187 return $actions;
1129 1188 }
1130 1189
1131 1190 $admin_url = get_admin_url( $user_blog->userblog_id );