PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.0.1
Defender Security – Malware Scanner, Login Security & Firewall v3.0.1
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 / security-tweaks / prevent-enum-users.php

prevent-enum-users.php in Defender Security – Malware Scanner, Login Security & Firewall 3.0.1, at src/component/security-tweaks/prevent-enum-users.php

253 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WP_Defender\Component\Security_Tweaks;
4
5 use Calotes\Base\Component;
6 use WP_Defender\Component\Security_Tweaks\Security_Key_Const_Interface;
7 use WP_Defender\Traits\Security_Tweaks_Option;
8
9 /**
10 * Class Prevent_Enum_Users
11 * @package WP_Defender\Component\Security_Tweaks
12 */
13 class Prevent_Enum_Users extends Component implements Security_Key_Const_Interface {
14
15 use Security_Tweaks_Option;
16
17 public $slug = 'prevent-enum-users';
18 public $resolved = false;
19
20 /**
21 * Enabled user enumerations list.
22 *
23 * @var array
24 */
25 private $enabled_user_enums = array();
26
27 /**
28 * Check whether the issue has been resolved or not.
29 *
30 * @return bool
31 */
32 public function check() {
33 return $this->resolved;
34 }
35
36 /**
37 * Here is the code for processing, if the return is true, we add it to resolve list, WP_Error if any error.
38 *
39 * @return bool
40 */
41 public function process() {
42 return true;
43 }
44
45 /**
46 * This is for un-do stuff that has been done in @process.
47 *
48 * @return bool
49 */
50 public function revert() {
51 return true;
52 }
53
54 /**
55 * Shield up.
56 *
57 * @return void
58 */
59 public function shield_up() {
60 $this->resolved = true;
61
62 if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
63 return;
64 }
65
66 add_filter( 'oembed_response_data', array( $this, 'oembed_author_filter' ), 99, 4 );
67 add_filter( 'wp_sitemaps_users_pre_url_list', array( $this, 'block_user_url_sitemap' ), 99, 0 );
68 add_filter( 'wp_sitemaps_add_provider', array( $this, 'block_user_provider_sitemap' ), 99, 2 );
69 add_filter( 'rest_authentication_errors', array( $this, 'block_rest_api_user_endpoint' ) );
70
71 if ( empty( $_SERVER['QUERY_STRING'] ) ) {
72 return;
73 }
74
75 $this->maybe_block( $_SERVER['QUERY_STRING'] );
76
77 add_filter( 'redirect_canonical', array( $this, 'maybe_block' ) );
78 }
79
80 /**
81 * Maybe block the request if it's trying to access the author page with query param.
82 *
83 * @param string $request
84 *
85 * @return string
86 */
87 public function maybe_block( $request ) {
88
89 if ( ! current_user_can( 'edit_others_posts' ) ) {
90 $message = __( 'Sorry, you are not allowed to access this page', 'wpdef' );
91 $title = __( 'Forbidden', 'wpdef' );
92 $status_code = 403;
93 $pattern = '/author=([0-9]*)|\?author=([0-9]*)(\/*)/i';
94
95 if ( preg_match( $pattern, $request ) ) {
96 wp_die( $message, $title, $status_code );
97 }
98 }
99
100 return $request;
101 }
102
103 /**
104 * Return a summary data of this tweak.
105 *
106 * @return array
107 */
108 public function to_array() {
109 return [
110 'slug' => $this->slug,
111 'title' => __( 'Prevent user enumeration', 'wpdef' ),
112 'errorReason' => __( 'User enumeration is currently allowed.', 'wpdef' ),
113 'successReason' => __( 'User enumeration is currently blocked, nice work!', 'wpdef' ),
114 'misc' => [],
115 'bulk_description' => __( 'To brute force your login, hackers and bots can simply type the query string ?author=1, ?author=2 and so on, which will redirect the page to /author/username/ - bam, the bot now has your usernames to begin brute force attacks with. We can add a .htaccess file to your site to prevent the redirection.', 'wpdef' ),
116 'bulk_title' => __( 'Prevent user enumeration', 'wpdef' )
117 ];
118 }
119
120 /**
121 * Getter method of enabled_user_enums.
122 *
123 * @return array Return array of enabled enumeration options.
124 */
125 public function get_enabled_user_enums() {
126 $enabled_user_enums = $this->get_option( 'enabled_user_enums' );
127
128 $this->enabled_user_enums = is_null( $enabled_user_enums )
129 ? array()
130 : (array) $enabled_user_enums;
131
132 return $this->enabled_user_enums;
133 }
134
135 /**
136 * Setter method of enabled_user_enums.
137 *
138 * @param array $value Array of enabled user enumeration options.
139 *
140 * @return bool Return true if value updated, otherwise false.
141 */
142 public function set_enabled_user_enums( $value ) {
143 $this->enabled_user_enums = (array) $value;
144
145 return $this->update_option(
146 'enabled_user_enums',
147 $this->enabled_user_enums
148 );
149 }
150
151 /**
152 * Filters the author detail from oEmbed response data.
153 *
154 * @param array $data The response data.
155 * @param WP_Post $post The post object.
156 * @param int $width The requested width.
157 * @param int $height The calculated height.
158 *
159 * @return array $data The response data.
160 */
161 public function oembed_author_filter( $data, $post, $width, $height ) {
162 if (
163 in_array(
164 'enum-oembed',
165 $this->get_enabled_user_enums(),
166 true
167 )
168 ) {
169 unset( $data['author_name'] );
170 unset( $data['author_url'] );
171 }
172
173 return $data;
174 }
175
176 /**
177 * Blocks user url generation in sitemap.
178 *
179 * Consider the URL: http://example.com/wp-sitemap-users-1.xml will not return user URL XML.
180 *
181 * @return bool|null If option sitemap enumeration enabled then return false else null/void will return.
182 */
183 public function block_user_url_sitemap() {
184 if (
185 in_array(
186 'enum-sitemap',
187 $this->get_enabled_user_enums(),
188 true
189 )
190 ) {
191 return false;
192 }
193 }
194
195 /**
196 * Blocks the user provider sitemap before it is added from wp-sitemap.xml.
197 *
198 * @param WP_Sitemaps_Provider $provider Instance of a WP_Sitemaps_Provider.
199 * @param string $name Name of the sitemap provider.
200 *
201 * @return bool|WP_Sitemaps_Provider If option sitemap enumeration enabled then return false else WP_Sitemaps_Provider instance.
202 */
203 public function block_user_provider_sitemap( $provider, $name ) {
204 if (
205 in_array(
206 'enum-sitemap',
207 $this->get_enabled_user_enums(),
208 true
209 ) &&
210 'users' === $name
211 ) {
212 return false;
213 }
214
215 return $provider;
216 }
217
218 /**
219 * Block user REST API endpoint for not logged-in access.
220 *
221 * @param WP_Error|null|true $errors WP_Error if authentication error, null if authentication
222 * method wasn't used, true if authentication succeeded.
223 *
224 * @return WP_Error|null|true $errors WP_Error if authentication error, null if authentication
225 * method wasn't used, true if authentication succeeded.
226 */
227 public function block_rest_api_user_endpoint( $errors ) {
228 $request_uri = filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL );
229 $rest_route = filter_input( INPUT_GET, 'rest_route', FILTER_SANITIZE_URL );
230
231 $is_url_match = preg_match( '/users/i', $request_uri ) !== 0 ||
232 preg_match( '/users/i', $rest_route ) !== 0;
233
234 if (
235 in_array(
236 'enum-rest',
237 $this->get_enabled_user_enums(),
238 true
239 ) &&
240 $is_url_match &&
241 ! is_user_logged_in()
242 ) {
243 return new \WP_Error(
244 'rest_cannot_access',
245 esc_html__( 'Only logged-in users can access the User endpoint REST API.', 'wpdef' ),
246 array( 'status' => rest_authorization_required_code() )
247 );
248 }
249
250 return $errors;
251 }
252 }
253