PluginProbe
Patchstack – WordPress & Plugins Security / 2.1.6
Patchstack – WordPress & Plugins Security v2.1.6
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/core.php +40 -154 2.1.232.1.6 View file →
@@ -91,30 +91,8 @@
91 91 )
92 92 );
93 93
94 94 /**
95 - * Some of the IP addresses of Patchstack.
96 - *
97 - * @var array
98 - */
99 - public $ips = array(
100 - '18.221.197.243',
101 - '52.15.237.250',
102 - '3.19.3.34',
103 - '3.18.238.17',
104 - '13.58.49.77',
105 - '18.222.191.77',
106 - '3.131.108.250',
107 - '3.23.157.140',
108 - '18.220.70.233',
109 - '3.140.84.221',
110 - '185.212.171.100',
111 - '3.133.121.93',
112 - '18.219.61.133',
113 - '3.14.29.150'
114 - );
115 -
116 - /**
117 95 * @param Patchstack $plugin
118 96 * @return void
119 97 */
120 98 public function __construct( $plugin ) {
@@ -197,161 +175,69 @@
197 175 return false;
198 176 }
199 177
200 178 /**
201 - * Grab the IP address of the user. Give the override IP header priority.
202 - * If this does not exist, we should always default to REMOTE_ADDR.
179 + * Determine if a given PHP function is disabled or not.
203 180 *
204 - * @return string
181 + * @param string $name Name of the function to check.
182 + * @return boolean Whether or not the function is available to call.
205 183 */
206 - public function get_ip() {
207 - $override = get_site_option( 'patchstack_firewall_ip_header', '' );
208 - if ( $override != '' && isset( $_SERVER[ $override ] ) ) {
209 - return $_SERVER[ $override ];
184 + public function function_available( $name ) {
185 + $safe_mode = ini_get( 'safe_mode' );
186 + if ( $safe_mode && strtolower( $safe_mode ) != 'off' ) {
187 + return false;
210 188 }
211 189
212 - return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
213 - }
214 -
215 - /**
216 - * Grab the secret key used for API communication.
217 - *
218 - * @param string $custom
219 - * @return string
220 - */
221 - public function get_secret_key( $custom = '' ) {
222 - if ( $custom != '' ) {
223 - return $this->encrypt( $custom );
190 + // Determine if the function is available.
191 + if ( in_array( $name, array_map( 'trim', explode( ',', ini_get( 'disable_functions' ) ) ) ) ) {
192 + return false;
224 193 }
225 194
226 - $secret = get_option( 'patchstack_secretkey', '' );
227 - if ( ! $secret ) {
228 - return '';
229 - }
230 -
231 - if ( strlen( $secret ) === 40 ) {
232 - $enc = $this->encrypt( $secret );
233 -
234 - update_option( 'patchstack_secretkey', $enc['cipher'] );
235 - update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
236 -
237 - return $secret;
238 - }
239 -
240 - $nonce = get_option( 'patchstack_secretkey_nonce' );
241 - return $this->decrypt( $secret, $nonce );
195 + return true;
242 196 }
243 197
244 198 /**
245 - * Set the secret key used for API communication.
246 - *
247 - * @param string $secret
248 - * @return void
249 - */
250 - public function set_secret_key( $secret ) {
251 - $enc = $this->encrypt( $secret );
252 -
253 - update_option( 'patchstack_secretkey', $enc['cipher'] );
254 - update_option( 'patchstack_secretkey_nonce', $enc['nonce'] );
255 - }
256 -
257 - /**
258 - * Determine which encryption dependency we can use.
259 - *
199 + * Attempt to get the client IP by checking all possible IP (proxy) headers.
200 + *
260 201 * @return string
261 202 */
262 - public function get_enc_type() {
263 - if ( function_exists('sodium_crypto_generichash') ) {
264 - return 'native';
203 + public function get_ip() {
204 + // IP address header override set?
205 + $override = get_site_option( 'patchstack_firewall_ip_header', '' );
206 + if ( $override != '' && isset( $_SERVER[ $override ] ) ) {
207 + return $_SERVER[ $override ];
265 208 }
266 209
267 - return 'compat';
268 - }
269 -
270 - /**
271 - * Get the unique nonce that is used for the secretbox.
272 - *
273 - * @return string
274 - */
275 - public function get_enc_nonce() {
276 - if ( function_exists('random_bytes') ) {
277 - return random_bytes( 24 );
210 + // IP address headers which should have priority and be used regardless of other headers.
211 + $priority = array( 'HTTP_CF_CONNECTING_IP', 'HTTP_X_SUCURI_CLIENTIP' );
212 + foreach ( $priority as $header ) {
213 + if ( isset( $_SERVER[ $header ] ) && filter_var( $_SERVER[ $header ], FILTER_VALIDATE_IP ) !== false ) {
214 + return $_SERVER[ $header ];
215 + }
278 216 }
279 217
280 - require_once dirname( __FILE__ ) . '/2fa/polyfill/lib/random.php';
281 - return random_bytes( 24 );
282 - }
218 + // Special case for hosts that have a weird configuration.
219 + if ( $this->function_available( 'php_uname' ) ) {
220 + $uname = @php_uname();
283 221
284 - /**
285 - * Encrypt a string.
286 - *
287 - * @param string $message
288 - * @return array
289 - */
290 - public function encrypt( $message ) {
291 - $enc_type = $this->get_enc_type();
292 - $nonce = $this->get_enc_nonce();
222 + // Bluehos and Hostmonster store the real IP in $_SERVER['REMOTE_ADDR'] but the proxy IP in HTTP_X_FORWARDED_FOR.t
223 + if ( strpos( $uname, 'bluehost' ) !== false || strpos( $uname, 'hostmonster' ) !== false ) {
224 + return $_SERVER['REMOTE_ADDR'];
225 + }
293 226
294 - try {
295 - // Use the PHP native encryption functions.
296 - if ( $enc_type == 'native' ) {
297 - $key = sodium_crypto_generichash( AUTH_KEY );
298 -
299 - return [
300 - 'cipher' => sodium_bin2hex( sodium_crypto_secretbox( $message, $nonce, $key ) ),
301 - 'nonce' => sodium_bin2hex( $nonce )
302 - ];
227 + // Hostgator stores the real IP in $_SERVER['REMOTE_ADDR'] but the proxy IP in HTTP_X_FORWARDED_FOR.
228 + if ( ( strpos( $uname, 'websitewelcome' ) || strpos( $uname, 'hostgator' ) ) && isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $_SERVER['REMOTE_ADDR'] ) {
229 + return $_SERVER['REMOTE_ADDR'];
303 230 }
304 -
305 - // Use the Sodium polyfill library part of WordPress core.
306 - require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
307 - $key = \Sodium\crypto_generichash( AUTH_KEY );
308 -
309 - return [
310 - 'cipher' => \Sodium\bin2hex( \Sodium\crypto_secretbox( $message, $nonce, $key ) ),
311 - 'nonce' => \Sodium\bin2hex( $nonce )
312 - ];
313 - } catch ( Exception $e ) {
314 - return [
315 - 'cipher' => $message,
316 - 'nonce' => ''
317 - ];
318 231 }
319 - }
320 232
321 - /**
322 - * Decrypt a cipher to plain-text.
323 - *
324 - * @param string $cipher
325 - * @param string $nonce
326 - * @return string
327 - */
328 - public function decrypt( $cipher, $nonce ) {
329 - $enc_type = $this->get_enc_type();
330 -
331 - // If we received an empty nonce, we assume it was never properly encrypted to begin with.
332 - if ( $nonce == '' ) {
333 - return $cipher;
334 - }
335 -
336 - try {
337 - // Determine if we should use native or polyfill functions.
338 - if ( $enc_type == 'native' ) {
339 - $key = sodium_crypto_generichash( AUTH_KEY );
340 - $dec = sodium_crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
341 - } else {
342 - require_once ABSPATH . WPINC . '/sodium_compat/autoload.php';
343 - $key = \Sodium\crypto_generichash( AUTH_KEY );
344 - $dec = \Sodium\crypto_secretbox_open( sodium_hex2bin( $cipher ), sodium_hex2bin( $nonce ), $key );
233 + // In order of priority, try to get the IP address.
234 + $allowed = array( 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'SUCURI_RIP', 'REMOTE_ADDR' );
235 + foreach ( $allowed as $header ) {
236 + if ( isset( $_SERVER[ $header ] ) && filter_var( $_SERVER[ $header ], FILTER_VALIDATE_IP ) !== false ) {
237 + return $_SERVER[ $header ];
345 238 }
346 - } catch ( Exception $e ) {
347 - return $cipher;
348 239 }
349 240
350 - // In case decryption failed, return null.
351 - if ( ! $dec ) {
352 - return null;
353 - }
354 -
355 - return $dec;
241 + return '127.0.0.1';
356 242 }
357 243 }