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/component/class-firewall.php +62 -15 5.3.1 → 6.3.0 View file →
@@ -14,9 +14,9 @@
14 14 use WP_Defender\Model\Lockout_Ip;
15 15 use WP_Defender\Behavior\WPMUDEV;
16 16 use WP_Defender\Model\Lockout_Log;
17 17 use WP_Defender\Model\Onboard;
18 -use MaxMind\Db\Reader\InvalidDatabaseException;
18 +use WP_DEFENDER_VENDOR\MaxMind\Db\Reader\InvalidDatabaseException;
19 19 use WP_Defender\Model\Setting\Firewall as Model_Firewall;
20 20 use WP_Defender\Component\Trusted_Proxy_Preset\Cloudflare_Proxy;
21 21 use WP_Defender\Component\Trusted_Proxy_Preset\Trusted_Proxy_Preset;
22 22 use WP_Defender\Controller\Firewall as Firewall_Controller;
@@ -63,18 +63,50 @@
63 63 private function is_commencing_staff_access( $access ): bool {
64 64 $action = defender_get_data_from_request( 'action', 'g' );
65 65 $wdpunkey = defender_get_data_from_request( 'wdpunkey', 'p' );
66 66
67 - return wp_doing_ajax() && 'wdpunauth' === $action && hash_equals( $wdpunkey, $access['key'] );
67 + return wp_doing_ajax() && 'wdpunauth' === $action && isset( $access['key'] ) && hash_equals( $wdpunkey, $access['key'] );
68 68 }
69 69
70 70 /**
71 71 * Check is the access from authenticated staff.
72 72 *
73 - * @return bool
73 + * Validates the `wpmudev_is_staff` cookie token by verifying:
74 + * - Token version and structure (v1.<payload>.<signature>)
75 + * - HMAC-SHA256 signature against site salt (`wpmudev_remote_access`)
76 + * - Token expiration timestamp against current time
77 + * - Audience (`aud`) against current site URL (`home_url('/')`)
78 + * - Subject (`sub`) against currently authenticated user ID
79 + *
80 + * @return bool True if staff access token is valid and unexpired, false otherwise.
74 81 */
75 82 private function is_authenticated_staff_access(): bool {
76 - return '1' === defender_get_data_from_request( 'wpmudev_is_staff', 'c' );
83 + $cookie = defender_get_data_from_request( 'wpmudev_is_staff', 'c' );
84 + if ( ! is_string( $cookie ) ) {
85 + return false;
86 + }
87 +
88 + $token = explode( '.', $cookie, 3 );
89 + if ( 3 !== count( $token ) || 'v1' !== $token[0] ) {
90 + return false;
91 + }
92 +
93 + $signature = hash_hmac( 'sha256', $token[1], wp_salt( 'wpmudev_remote_access' ) );
94 + if ( ! hash_equals( $signature, $token[2] ) ) {
95 + return false;
96 + }
97 + // Improve flow to avoid bypassing staff access authentication via an unsigned cookie. Use legitim payload.
98 + $payload = base64_decode( strtr( $token[1], '-_', '+/' ) . str_repeat( '=', ( 4 - strlen( $token[1] ) % 4 ) % 4 ), true );// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
99 + $payload = is_string( $payload ) ? json_decode( $payload, true ) : null;
100 +
101 + return is_array( $payload ) &&
102 + isset( $payload['aud'], $payload['exp'], $payload['sub'] ) &&
103 + is_string( $payload['aud'] ) &&
104 + is_numeric( $payload['exp'] ) &&
105 + is_numeric( $payload['sub'] ) &&
106 + (int) $payload['exp'] > time() &&
107 + hash_equals( home_url( '/' ), $payload['aud'] ) &&
108 + get_current_user_id() === (int) $payload['sub'];
77 109 }
78 110
79 111 /**
80 112 * Check if the access is from our staff access.
@@ -85,15 +117,24 @@
85 117 if ( defined( 'WPMUDEV_DISABLE_REMOTE_ACCESS' ) && true === constant( 'WPMUDEV_DISABLE_REMOTE_ACCESS' ) ) {
86 118 return false;
87 119 }
88 120
89 - $wpmu_dev = new WPMUDEV();
90 - $is_remote_access = $wpmu_dev->get_apikey() &&
121 + $wpmu_dev = new WPMUDEV();
122 +
123 + // Remote staff access needs Dashboard mode.
124 + if ( 'dashboard' !== $wpmu_dev->resolve_connection_mode() ) {
125 + return false;
126 + }
127 +
128 + $is_remote_access = is_object( WPMUDEV_Dashboard::$api ) &&
91 129 true === WPMUDEV_Dashboard::$api->remote_access_details( 'enabled' );
92 130
93 131 if ( $is_remote_access ) {
94 - $access = $wpmu_dev->get_remote_access();
95 - if ( $this->is_authenticated_staff_access() || $this->is_commencing_staff_access( $access ) ) {
132 + $access = $wpmu_dev->get_remote_access();
133 + $is_authenticated_staff_access = isset( $access['userid'] ) &&
134 + get_current_user_id() === (int) $access['userid'] &&
135 + $this->is_authenticated_staff_access();
136 + if ( $is_authenticated_staff_access || $this->is_commencing_staff_access( $access ) ) {
96 137 $this->log( $access, Firewall_Controller::FIREWALL_LOG );
97 138
98 139 return true;
99 140 }
@@ -114,9 +155,12 @@
114 155 return;
115 156 }
116 157 $time_string = '-' . $storage_days . ' days';
117 158 $timestamp = $this->local_to_utc( $time_string );
118 - Lockout_Log::remove_logs( $timestamp, 50 );
159 + // Since v5.7.0.
160 + $delete_count = apply_filters( 'wpdef_firewall_limit_deleted_logs', 50 );
161 + $delete_count = is_int( $delete_count ) ? $delete_count : (int) $delete_count;
162 + Lockout_Log::remove_logs( $timestamp, $delete_count );
119 163 }
120 164
121 165 /**
122 166 * Cron for clean up temporary IP block list.
@@ -269,9 +313,10 @@
269 313 * @return int
270 314 * @since 3.7.0 Get the limit of Lockout records.
271 315 */
272 316 public function get_lockout_record_limit() {
273 - return (int) apply_filters( 'wd_lockout_record_limit', 10000 );
317 + $limit = apply_filters( 'wd_lockout_record_limit', 10000 );
318 + return is_int( $limit ) ? $limit : (int) $limit;
274 319 }
275 320
276 321 /**
277 322 * Cron for deleting unwanted lockout records.
@@ -325,9 +370,9 @@
325 370
326 371 $gathered_ips = array();
327 372 $server = defender_get_data_from_request( null, 's' );
328 373 foreach ( $ip_headers as $header ) {
329 - if ( ! empty( $server[ $header ] ) ) {
374 + if ( isset( $server[ $header ] ) && '' !== $server[ $header ] ) {
330 375 // Handle multiple IP addresses.
331 376 $ips = array_map( 'trim', explode( ',', $server[ $header ] ) );
332 377
333 378 foreach ( $ips as $ip ) {
@@ -345,9 +390,9 @@
345 390 *
346 391 * @since 4.5.1
347 392 * @deprecated 5.1.0 No longer used and will be removed in a future version.
348 393 */
349 - $gathered_ips = (array) apply_filters_deprecated(
394 + $gathered_ips = apply_filters_deprecated(
350 395 'wpdef_firewall_gathered_ips',
351 396 array_unique( $gathered_ips ),
352 397 '5.1.0',
353 398 '',
@@ -352,8 +397,9 @@
352 397 '5.1.0',
353 398 '',
354 399 'This filter will be removed in a future version. If you are using it, update your implementation.'
355 400 );
401 + $gathered_ips = ! is_array( $gathered_ips ) ? (array) $gathered_ips : $gathered_ips;
356 402
357 403 return $this->filter_user_ips( $gathered_ips );
358 404 }
359 405
@@ -409,9 +455,9 @@
409 455 * @return void
410 456 */
411 457 public function update_trusted_proxy_preset_ips(): void {
412 458 $model = wd_di()->get( Model_Firewall::class );
413 - if ( ! empty( $model->trusted_proxy_preset ) ) {
459 + if ( '' !== $model->trusted_proxy_preset ) {
414 460 /**
415 461 * Retrieve Trusted_Proxy_Preset instance.
416 462 *
417 463 * @var Trusted_Proxy_Preset $trusted_proxy_preset
@@ -570,9 +616,10 @@
570 616 * @param bool $enable True to enable whitelist server public IP, false otherwise.
571 617 *
572 618 * @since 5.0.2
573 619 */
574 - return (bool) apply_filters( 'wpdef_firewall_whitelist_server_public_ip_enabled', true );
620 + $enabled = apply_filters( 'wpdef_firewall_whitelist_server_public_ip_enabled', true );
621 + return is_bool( $enabled ) ? $enabled : (bool) $enabled;
575 622 }
576 623
577 624 /**
578 625 * Set whitelist server public IP.
@@ -585,9 +632,9 @@
585 632 return false;
586 633 }
587 634
588 635 $ip = wd_di()->get( Smart_Ip_Detection::class )->get_server_public_ip();
589 - if ( empty( $ip ) ) {
636 + if ( '' === $ip ) {
590 637 $this->log( 'Failed to whitelist server public IP.', Firewall_Controller::FIREWALL_LOG );
591 638 return false;
592 639 }
593 640