PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.7
Patchstack – WordPress & Plugins Security v2.2.7
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
← All changes | includes/hardening.php +52 -33 2.1.212.2.7 View file →
@@ -19,9 +19,9 @@
19 19 public function __construct( $core ) {
20 20 parent::__construct( $core );
21 21
22 22 // Auto update plugins.
23 - add_action( 'patchstack_update_plugins', array( $this, 'update_vulnerable_plugins' ) );
23 + add_action( 'patchstack_update_plugins', [ $this, 'update_vulnerable_plugins' ] );
24 24
25 25 // The hardening features can only be used on an activated license.
26 26 if ( ! $this->license_is_active() || $this->get_option( 'patchstack_license_free', 0 ) == 1 ) {
27 27 return;
@@ -32,19 +32,19 @@
32 32 define( 'DISALLOW_FILE_EDIT', 1 );
33 33 }
34 34
35 35 // Set security headers
36 - add_filter( 'wp_headers', array( $this, 'set_security_headers' ), 10, 1 );
36 + add_filter( 'wp_headers', [ $this, 'set_security_headers' ], 10, 1 );
37 37
38 38 // When country blocking is set.
39 - if ( $this->get_option( 'patchstack_geo_block_enabled', false ) && ! empty( $this->get_option( 'patchstack_geo_block_countries', array() ) ) ) {
40 - add_action( 'init', array( $this, 'geo_block_check' ), 10 );
39 + if ( $this->get_option( 'patchstack_geo_block_enabled', false ) && ! empty( $this->get_option( 'patchstack_geo_block_countries', [] ) ) ) {
40 + add_action( 'init', array( $this, 'geo_block_check' ), ~PHP_INT_MAX );
41 41 }
42 42
43 43 // Apply comment captcha?
44 44 if ( $this->get_option( 'patchstack_captcha_on_comments', 0 ) && ! is_user_logged_in() ) {
45 - add_action( 'comment_form_after_fields', array( $this, 'captcha_display' ) );
46 - add_filter( 'preprocess_comment', array( $this, 'verify_recaptcha' ) );
45 + add_action( 'comment_form_after_fields', [ $this, 'captcha_display' ] );
46 + add_filter( 'preprocess_comment', [ $this, 'verify_recaptcha' ] );
47 47 }
48 48
49 49 // Disable the application passwords feature?
50 50 if ( $this->get_option( 'patchstack_application_passwords_disabled', false ) == true ) {
@@ -57,30 +57,30 @@
57 57 }
58 58
59 59 // Block unauthorized wp-json requests?
60 60 if ( $this->get_option( 'patchstack_json_is_disabled', false ) ) {
61 - add_filter( 'rest_authentication_errors', array( $this, 'disable_wpjson' ) );
61 + add_filter( 'rest_authentication_errors', [ $this, 'disable_wpjson' ] );
62 62 }
63 63
64 64 // Prevent user enumeration?
65 65 if ( $this->get_option( 'patchstack_userenum' ) ) {
66 - add_action( 'init', array( $this, 'stop_user_enum' ), 1 );
66 + add_action( 'init', [ $this, 'stop_user_enum' ], 1 );
67 67 }
68 68
69 69 // Attempt to hide the WordPress version?
70 70 if ( $this->get_option( 'patchstack_hidewpversion' ) ) {
71 71 remove_action( 'wp_head', 'wp_generator' );
72 - add_filter( 'the_generator', array( $this, 'remove_generator' ) );
72 + add_filter( 'the_generator', [ $this, 'remove_generator' ] );
73 73 }
74 74
75 75 // Block email registration patterns?
76 76 if ( $this->get_option( 'patchstack_register_email_blacklist', '' ) != '' ) {
77 - add_filter( 'registration_errors', array( $this, 'check_email_pattern' ), 1, 3 );
78 - add_filter( 'wpmu_validate_user_signup', array( $this, 'check_email_pattern_wpmu' ), 1, 1 );
77 + add_filter( 'registration_errors', [ $this, 'check_email_pattern' ], 1, 3 );
78 + add_filter( 'wpmu_validate_user_signup', [ $this, 'check_email_pattern_wpmu' ], 1, 1 );
79 79 }
80 80
81 81 // Auto update software?
82 - $update = get_site_option( 'patchstack_auto_update', array() );
82 + $update = get_site_option( 'patchstack_auto_update', [] );
83 83 if ( is_array( $update ) ) {
84 84 foreach ( $update as $type ) {
85 85 if ( $type != 'vulnerable' ) {
86 86 add_filter( 'auto_update_' . $type, '__return_true' );
@@ -97,15 +97,15 @@
97 97 * @return void
98 98 */
99 99 public function update_vulnerable_plugins() {
100 100 // Is the auto update setting for vulnerable plugins enabled?
101 - $update = get_site_option( 'patchstack_auto_update', array() );
101 + $update = get_site_option( 'patchstack_auto_update', [] );
102 102 if ( ! is_array( $update ) || ! in_array( 'vulnerable', $update ) ) {
103 103 return;
104 104 }
105 105
106 106 // Do we even have any vulnerable plugins to auto update?
107 - $plugins = get_site_option( 'patchstack_vulnerable_plugins', array() );
107 + $plugins = get_site_option( 'patchstack_vulnerable_plugins', [] );
108 108 if ( ! is_array( $plugins ) || count( $plugins ) == 0 ) {
109 109 return;
110 110 }
111 111
@@ -124,9 +124,9 @@
124 124 @wp_update_plugins();
125 125 $all_plugins = get_plugins();
126 126
127 127 // New array with all available plugins and the ones we want to upgrade.
128 - $upgrade = array();
128 + $upgrade = [];
129 129 foreach ( $all_plugins as $path => $data ) {
130 130 if ( in_array( $path, $plugins ) ) {
131 131 array_push( $upgrade, $path );
132 132 }
@@ -136,9 +136,9 @@
136 136 $upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
137 137 $upgrader->bulk_upgrade( $upgrade );
138 138
139 139 // Reset the option that holds the vulnerable plugins.
140 - update_site_option( 'patchstack_vulnerable_plugins', array() );
140 + update_site_option( 'patchstack_vulnerable_plugins', [] );
141 141
142 142 // Resend the sofware data to the API.
143 143 do_action( 'patchstack_send_software_data' );
144 144 }
@@ -148,14 +148,23 @@
148 148 *
149 149 * @return void
150 150 */
151 151 public function geo_block_check() {
152 - $countries = $this->get_option( 'patchstack_geo_block_countries', array() );
152 + $countries = $this->get_option( 'patchstack_geo_block_countries', [] );
153 153 $ip = $this->get_ip();
154 154
155 155 // Don't block Patchstack.
156 - if ( in_array( $_SERVER['REMOTE_ADDR'], $this->ips ) || ( isset( $_POST['webarx_secret'] ) && $this->plugin->listener->verifyToken( $_POST['webarx_secret'] ) ) ) {
157 - return;
156 + if ( in_array( $ip, $this->ips ) || ( isset( $_POST['webarx_secret'] ) && $this->plugin->listener->verifyToken( $_POST['webarx_secret'] ) ) || isset( $_POST['patchstack_ott_action'] )) {
157 +
158 + // OTT action.
159 + if ( isset( $_POST['patchstack_ott_action'] ) ) {
160 + $ott = get_option( 'patchstack_ott_action', '' );
161 + if ( ! empty( $ott ) && hash_equals( $ott, $_POST['patchstack_ott_action'] ) ) {
162 + return;
163 + }
164 + } else {
165 + return;
166 + }
158 167 }
159 168
160 169 // Load the required libraries.
161 170 try {
@@ -180,11 +189,21 @@
180 189 *
181 190 * @return void|WP_Error
182 191 */
183 192 public function disable_wpjson() {
193 + // Some default exceptions.
194 + $path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
195 + $whitelists = [ '/wp-json/contact-form-7/' ];
196 + foreach ( $whitelists as $whitelist ) {
197 + if ( stripos( $path, $whitelist ) !== false ) {
198 + return;
199 + }
200 + }
201 +
202 + // Block unauthorized users.
184 203 if ( ! is_user_logged_in() ) {
185 204 $msg = apply_filters( 'disable_wp_rest_api_error', __( 'The WP REST API cannot be accessed by unauthorized users.', 'disable-wp-rest-api' ) );
186 - return new WP_Error( 'rest_authorization_required', $msg, array( 'status' => rest_authorization_required_code() ) );
205 + return new WP_Error( 'rest_authorization_required', $msg, [ 'status' => rest_authorization_required_code() ] );
187 206 }
188 207 }
189 208
190 209 /**
@@ -266,33 +285,33 @@
266 285 break;
267 286 }
268 287
269 288 if ( ! $secret_key || ! $site_key ) {
270 - return array(
289 + return [
271 290 'response' => false,
272 291 'reason' => 'ERROR_NO_KEYS',
273 - );
292 + ];
274 293 }
275 294
276 295 if ( ! isset( $_POST['g-recaptcha-response'] ) || empty( $_POST['g-recaptcha-response'] ) ) {
277 - return array(
296 + return [
278 297 'response' => false,
279 298 'reason' => 'RECAPTCHA_EMPTY_RESPONSE',
280 - );
299 + ];
281 300 }
282 301
283 302 $response = $this->get_captcha_response( $secret_key );
284 303 if ( isset( $response['success'] ) && ! empty( $response['success'] ) ) {
285 - return array(
304 + return [
286 305 'response' => true,
287 306 'reason' => '',
288 - );
307 + ];
289 308 }
290 309
291 - return array(
310 + return [
292 311 'response' => false,
293 312 'reason' => 'VERIFICATION_FAILED',
294 - );
313 + ];
295 314 }
296 315
297 316 /**
298 317 * Query Google for reAPTCHA validation and response.
@@ -300,15 +319,15 @@
300 319 * @param string $privatekey
301 320 * @return array
302 321 */
303 322 public function get_captcha_response( $privatekey ) {
304 - $args = array(
305 - 'body' => array(
323 + $args = [
324 + 'body' => [
306 325 'secret' => $privatekey,
307 326 'response' => $_POST['g-recaptcha-response'],
308 - ),
327 + ],
309 328 'sslverify' => false,
310 - );
329 + ];
311 330 $resp = wp_remote_post( 'https://www.google.com/recaptcha/api/siteverify', $args );
312 331 return json_decode( wp_remote_retrieve_body( $resp ), true );
313 332 }
314 333
@@ -333,9 +352,9 @@
333 352 *
334 353 * @return void
335 354 */
336 355 public function stop_user_enum() {
337 - if ( isset( $_GET['author'] ) && is_numeric( $_GET['author'] ) && ! is_user_logged_in() ) {
356 + if ( isset( $_GET['author'] ) && ! is_user_logged_in() && ! is_admin() ) {
338 357 die( wp_safe_redirect( get_site_url() ) );
339 358 }
340 359
341 360 if ( stripos( $_SERVER['REQUEST_URI'], 'v2/users' ) !== false || ( isset( $_REQUEST['rest_route'] ) && stripos( $_REQUEST['rest_route'], 'v2/users' ) !== false ) ) {