Server IP : 104.21.14.48 / Your IP : 3.135.208.201 [ Web Server : Apache System : Linux b70eb322-3aee-0c53-7c82-0db91281f2c6.secureserver.net 6.1.90-1.el9.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 2 12:09:22 EDT 2024 x86_64 User : root ( 0) PHP Version : 8.0.30.2 Disable Function : NONE Domains : 0 Domains MySQL : ON | cURL : ON | WGET : ON | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/wp-content/plugins/defender-security/src/component/ |
Upload File : |
<?php /** * Methods for generating cryptographically secure pseudo-random bytes and integers, comparing strings securely, * and encrypting/decrypting data. * * @package WP_Defender\Component */ namespace WP_Defender\Component; use WP_Error; use Exception; use SodiumException; use RuntimeException; use WP_Defender\Traits\IO; use Calotes\Base\Component; /** * Methods for generating cryptographically secure pseudo-random bytes and integers, comparing strings securely, * and encrypting/decrypting data. * * @since 3.3.1 */ class Crypt extends Component { use IO; /** * Generates cryptographically secure pseudo-random bytes. * * @param int $bytes The number of bytes to generate. * * @return string */ public static function random_bytes( int $bytes ): string { // Try with random_bytes. if ( function_exists( 'random_bytes' ) ) { try { $rand = random_bytes( $bytes ); if ( is_string( $rand ) && strlen( $rand ) === $bytes ) { return $rand; } } catch ( Exception $e ) { $_this = new self(); $_this->log( $e->getMessage(), 'internal.log' ); } } // Try with openssl_random_pseudo_bytes. if ( function_exists( 'openssl_random_pseudo_bytes' ) ) { $rand = openssl_random_pseudo_bytes( $bytes, $strong ); if ( is_string( $rand ) && strlen( $rand ) === $bytes ) { return $rand; } } // Not safe. Use in extreme cases. $return = ''; for ( $i = 0; $i < $bytes; $i++ ) { // Using wp_rand cause backward compatibility issues. $return .= chr( mt_rand( 0, 255 ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_mt_rand } return $return; } /** * Generates cryptographically secure pseudo-random integers. * * @param int $min The minimum value of the generated integer (inclusive). * @param int $max The maximum value of the generated integer (inclusive). * * @return int * @throws RuntimeException On failure. */ public static function random_int( $min = 0, $max = 0x7FFFFFFF ): int { if ( function_exists( 'random_int' ) ) { try { return random_int( $min, $max ); } catch ( Exception $e ) { $_this = new self(); $_this->log( $e->getMessage(), 'internal.log' ); } } $diff = $max - $min; $bytes = self::random_bytes( 4 ); if ( 4 !== strlen( $bytes ) ) { throw new RuntimeException( 'Unable to get 4 bytes' ); } $val = unpack( 'nint', $bytes ); $val = $val['int'] & 0x7FFFFFFF; // Convert to [0,1]. $fp = (float) $val / 2147483647.0; return (int) ( round( $fp * $diff ) + $min ); } /** * Compare two strings to avoid timing attacks. * * @param string $expected The expected string. * @param string $actual The actual string to compare against expected. * * @return bool */ public static function compare_lines( $expected, $actual ): bool { if ( function_exists( 'hash_equals' ) ) { return hash_equals( $expected, $actual ); } $len_expected = mb_strlen( $expected, '8bit' ); $len_actual = mb_strlen( $actual, '8bit' ); $len = min( $len_expected, $len_actual ); $result = 0; for ( $i = 0; $i < $len; $i++ ) { $result |= ord( $expected[ $i ] ) ^ ord( $actual[ $i ] ); } $result |= $len_expected ^ $len_actual; return 0 === $result; } /** * Encrypts a given string using a specified key. * * @param string $value The plaintext value to encrypt. * @param string $key The encryption key. * * @return string|WP_Error Returns the encrypted string or WP_Error on failure. * @throws SodiumException Throws an exception if encryption fails. */ private static function encrypt( $value, $key ) { $key = base64_decode( $key ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode if ( SODIUM_CRYPTO_SECRETBOX_KEYBYTES !== mb_strlen( $key, '8bit' ) ) { return new WP_Error( Error_Code::ENCRYPT_ERROR, esc_html__( 'The issue with Sodium library.', 'defender-security' ) ); } $nonce = self::random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES ); $ciphertext = sodium_crypto_secretbox( $value, $nonce, $key ); return base64_encode( $nonce . $ciphertext ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode } /** * Decrypts an encrypted string using a specified key. * * @param string $encoded_value The encrypted data to decrypt. * @param string $key The decryption key. * * @return string|WP_Error Returns the decrypted string or WP_Error on failure. * @throws SodiumException Throws an exception if decryption fails. */ private static function decrypt( $encoded_value, $key ) { if ( ! $encoded_value || '' === $key ) { return new WP_Error( Error_Code::DECRYPT_ERROR, esc_html__( 'Please re-setup 2FA TOTP method again.', 'defender-security' ) ); } $key = base64_decode( $key ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode $decoded = base64_decode( $encoded_value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode $nonce = mb_substr( $decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit' ); $ciphertext = mb_substr( $decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit' ); $decrypted = sodium_crypto_secretbox_open( $ciphertext, $nonce, $key ); if ( false === $decrypted ) { return new WP_Error( Error_Code::DECRYPT_ERROR, esc_html__( 'Please re-setup 2FA TOTP method again.', 'defender-security' ) ); } return $decrypted; } /** * Get the path to a file with a random key. This is used for 2FA TOTP. * * @return string */ public static function get_path_to_key_file() { return wp_normalize_path( WP_CONTENT_DIR ) . DIRECTORY_SEPARATOR . 'wp-defender-secrets.php'; } /** * Decrypts data using a stored random key. * * @param string $data The encrypted data to decrypt. * * @return string|WP_Error Returns the decrypted data or WP_Error on failure. * @throws SodiumException Throws an exception if decryption fails. */ public static function get_decrypted_data( $data ) { $key = self::get_random_key(); if ( is_wp_error( $key ) ) { return $key; } return self::decrypt( $data, $key ); } /** * Encrypts data using a stored random key. * * @param string $data The plaintext data to encrypt. * * @return string|WP_Error Returns the encrypted data or WP_Error on failure. * @throws SodiumException Throws an exception if encryption fails. */ public static function get_encrypted_data( $data ) { $key = self::get_random_key(); if ( is_wp_error( $key ) ) { return $key; } return self::encrypt( $data, $key ); } /** * Retrieves a random cryptographic key from a file. * * @return string|WP_Error Returns the cryptographic key or WP_Error if the key file is not found or invalid. * @throws SodiumException Throws an exception if key retrieval fails. */ private static function get_random_key() { $file = self::get_path_to_key_file(); if ( ! file_exists( $file ) ) { return new WP_Error( Error_Code::IS_EMPTY, esc_html__( 'The Defender file with the random key does not exist.', 'defender-security' ) ); } if ( ! defined( 'WP_DEFENDER_TOTP_KEY' ) ) { require_once $file; } if ( '{{__REPLACE_CODE__}}' !== constant( 'WP_DEFENDER_TOTP_KEY' ) ) { return WP_DEFENDER_TOTP_KEY; } else { return new WP_Error( Error_Code::INVALID, esc_html__( 'The Defender file with the random key is incorrect.', 'defender-security' ) ); } } /** * Generate a random key. * * @return string * @throws Exception On failure. */ protected function generate_random_key(): string { return base64_encode( sodium_crypto_secretbox_keygen() ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode } /** * Create a file with a random key. * * @return bool * @throws Exception On failure. */ public function create_key_file(): bool { global $wp_filesystem; // Initialize the WP filesystem, no more using 'file-put-contents' function. if ( empty( $wp_filesystem ) ) { require_once ABSPATH . '/wp-admin/includes/file.php'; WP_Filesystem(); } $to = self::get_path_to_key_file(); if ( ! file_exists( $to ) ) { // Move a template file to WP_CONTENT and replace the file content. $template_file = WP_DEFENDER_DIR . 'src' . DIRECTORY_SEPARATOR . 'component' . DIRECTORY_SEPARATOR . 'wp-defender-sample.php'; if ( copy( $template_file, $to ) ) { $content = $wp_filesystem->get_contents( $to ); if ( false !== strpos( $content, '{{__REPLACE_CODE__}}' ) ) { $new_content = str_replace( '{{__REPLACE_CODE__}}', $this->generate_random_key(), $content ); return (bool) file_put_contents( $to, $new_content, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents } } // The file was not copied. return false; } // Everything is fine. The file exists. return true; } }