PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.2.2
Defender Security – Malware Scanner, Login Security & Firewall v6.2.2
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 / component / class-mask-login.php

class-mask-login.php in Defender Security – Malware Scanner, Login Security & Firewall 6.2.2, at src/component/class-mask-login.php

217 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles the logic for masking the login URL to enhance security by obscuring the default login paths.
4 *
5 * @package WP_Defender\Component
6 */
7
8 namespace WP_Defender\Component;
9
10 use WP_Defender\Component;
11 use WP_Recovery_Mode_Link_Service;
12 use WP_Defender\Helper\Request;
13
14 /**
15 * Handles the logic for masking the login URL to enhance security by obscuring the default login paths.
16 */
17 class Mask_Login extends Component {
18
19 /**
20 * Check if the current user is land on login page, then we can start the block.
21 *
22 * @param string $requested_path The path requested by the user.
23 *
24 * @return bool
25 */
26 public function is_on_login_page( $requested_path ): bool {
27 // Decoded url path, e.g. for case 'wp-%61dmin'.
28 $requested_path = rawurldecode( strtolower( $requested_path ) );
29 $login_slugs = apply_filters(
30 'wd_login_strict_slugs',
31 array(
32 'wp-admin',
33 'wp-login',
34 'wp-login.php',
35 )
36 );
37 foreach ( $login_slugs as $slug ) {
38 if ( false !== stristr( $requested_path, "$slug" ) ) {
39 return true;
40 }
41 }
42 $login_slugs = apply_filters(
43 'wd_login_slugs',
44 array(
45 'login',
46 'dashboard',
47 'admin',
48 // Because WP redirects from 'login/' to the login page.
49 'login/',
50 )
51 );
52
53 // Check the request path contains default login text.
54 if ( in_array( $requested_path, $login_slugs, true ) ) {
55 return true;
56 }
57
58 return false;
59 }
60
61 /**
62 * Check if the request is land on masked URL.
63 *
64 * @param string $masked_url The custom masked URL set by the user.
65 *
66 * @return bool
67 */
68 public function is_land_on_masked_url( $masked_url ): bool {
69 return ltrim( rtrim( $this->get_request_path(), '/' ), '/' ) === ltrim( rtrim( $masked_url, '/' ), '/' );
70 }
71
72 /**
73 * Get the current request URI.
74 *
75 * @param null|string $site_url This only need in unit test.
76 *
77 * @return mixed
78 */
79 public function get_request_path( $site_url = null ) {
80 if ( null === $site_url ) {
81 $site_url = $this->get_site_url();
82 }
83
84 $request_uri = Request::get_request_uri();
85
86 // If parsed URL is null. PHP v8.1 displays it as deprecated.
87 $parsed_url = wp_parse_url( $site_url, PHP_URL_PATH );
88 $path = false === $parsed_url || null === $parsed_url ? '' : $parsed_url;
89
90 if ( strlen( $path ) && 0 === strpos( $request_uri, $path ) ) {
91 $request_uri = substr( $request_uri, strlen( $path ) );
92 }
93 $request_uri = '/' . ltrim( $request_uri, '/' );
94
95 return wp_parse_url( $request_uri, PHP_URL_PATH );
96 }
97
98 /**
99 * Copy cat from the function get_site_url without the filter.
100 *
101 * @param int|null $blog_id Optional. The blog ID in a multisite environment. Default is null.
102 * @param string $path Optional. Additional path to append to the site URL. Default is an empty string.
103 * @param string|null $scheme Optional. The scheme to use. Default is null.
104 *
105 * @return string The unfiltered site URL.
106 */
107 private function get_site_url( $blog_id = null, $path = '', $scheme = null ) {
108 if ( is_null( $blog_id ) || ! is_multisite() ) {
109 $url = get_option( 'siteurl' );
110 } else {
111 switch_to_blog( $blog_id );
112 $url = get_option( 'siteurl' );
113 restore_current_blog();
114 }
115
116 $url = set_url_scheme( $url, $scheme );
117
118 if ( $path && is_string( $path ) ) {
119 $url .= '/' . ltrim( $path, '/' );
120 }
121
122 return $url;
123 }
124
125 /**
126 * Redeems a ticket that allows bypassing the masked login.
127 *
128 * @param string $ticket The ticket to redeem.
129 *
130 * @return bool Returns true if the ticket is successfully redeemed, false if the ticket is invalid or expired.
131 */
132 public function redeem_ticket( $ticket ): bool {
133 $settings = wd_di()->get( \WP_Defender\Model\Setting\Mask_Login::class );
134 $detail = $settings->express_tickets[ $ticket ] ?? false;
135 if ( false === $detail ) {
136 return false;
137 }
138
139 // Ticket expired.
140 if ( $detail['expiry'] < time() ) {
141 unset( $settings->express_tickets[ $ticket ] );
142 $settings->save();
143
144 return false;
145 }
146
147 $detail['used'] += 1;
148 $settings->express_tickets[ $ticket ] = $detail;
149 $settings->save();
150
151 return true;
152 }
153
154 /**
155 * Check if locale should be set.
156 *
157 * @param string $mask_url The Mask URL slug.
158 *
159 * @return bool
160 * @since 3.12.0
161 */
162 public function is_set_locale( string $mask_url ): bool {
163 $lang = defender_get_data_from_request( 'wp_lang', 'g' );
164 return $this->is_land_on_masked_url( $mask_url ) && ! is_null( $lang ) && '' !== $lang;
165 }
166
167 /**
168 * Set locale on Mask Login page.
169 *
170 * @return void
171 * @since 3.12.0
172 */
173 public function set_locale(): void {
174 $wp_lang = defender_get_data_from_request( 'wp_lang', 'g' );
175
176 if ( ! is_null( $wp_lang ) && '' !== $wp_lang ) {
177 switch_to_locale( $wp_lang );
178 }
179 }
180
181 /**
182 * Check if this is a login page.
183 *
184 * @return bool
185 * @since 4.1.0
186 */
187 public function is_login_url(): bool {
188 $is_login_path = wp_parse_url( wp_login_url(), PHP_URL_PATH );
189 $requested_path = $this->get_request_path();
190
191 return trim( $requested_path, '/' ) === trim( $is_login_path, '/' );
192 }
193
194 /**
195 * Provides the login URL, which might be masked if the feature is active.
196 *
197 * @return string The login URL, potentially masked.
198 */
199 public static function maybe_masked_login_url(): string {
200 $settings = wd_di()->get( \WP_Defender\Model\Setting\Mask_Login::class );
201 if ( $settings->is_active() ) {
202 return $settings->get_new_login_url();
203 } else {
204 return wp_login_url();
205 }
206 }
207
208 /**
209 * Is user hit the recovery link?
210 *
211 * @return bool
212 */
213 public function is_recovery_mode(): bool {
214 return WP_Recovery_Mode_Link_Service::LOGIN_ACTION_ENTER === defender_get_data_from_request( 'action', 'r' );
215 }
216 }
217