| @@ -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 | } |