public function generate_new_salts() { if (!ITSEC_Modules::get_setting('global', 'write_files')) { return new WP_Error('itsec-wordpress-salts-utilities-write-files-disabled', __('The "Write to Files" setting is disabled in Global Settings. In order to use this feature, you must enable the "Write to Files" setting.', 'better-wp-security')); } require_once ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-config-file.php'; require_once ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-file.php'; $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path(); $config = ITSEC_Lib_File::read($config_file_path); if (is_wp_error($config)) { return new WP_Error('itsec-wordpress-salts-utilities-cannot-read-wp-config.php', sprintf(__('Unable to read the <code>wp-config.php</code> file in order to update the salts. You will need to manually update the file. Error details as follows: %1$s (%2$s)', 'better-wp-security'), $config->get_error_message(), $config->get_error_code())); } $defines = array('AUTH_KEY', 'SECURE_AUTH_KEY', 'LOGGED_IN_KEY', 'NONCE_KEY', 'AUTH_SALT', 'SECURE_AUTH_SALT', 'LOGGED_IN_SALT', 'NONCE_SALT'); foreach ($defines as $define) { if (empty($salts)) { $salts = self::get_new_salts(); } $salt = array_pop($salts); if (empty($salt)) { $salt = wp_generate_password(64, true, true); } $salt = str_replace('$', '\\$', $salt); $regex = "/(define\\s*\\(\\s*(['\"]){$define}\\2\\s*,\\s*)(['\"]).+?\\3(\\s*\\)\\s*;)/"; $config = preg_replace($regex, "\${1}'{$salt}'\${4}", $config); } $write_result = ITSEC_Lib_File::write($config_file_path, $config); if (is_wp_error($write_result)) { return new WP_Error('itsec-wordpress-salts-utilities-cannot-save-wp-config.php', sprintf(__('Unable to update the <code>wp-config.php</code> file in order to update the salts. You will need to manually update the file. Error details as follows: %1$s (%2$s)', 'better-wp-security'), $config->get_error_message(), $config->get_error_code())); } return true; }
/** * Sanitize and validate input * */ public function process_database_prefix() { global $wpdb, $itsec_files; //suppress error messages due to timing error_reporting(0); @ini_set('display_errors', 0); $check_prefix = true; //Assume the first prefix we generate is unique //generate a new table prefix that doesn't conflict with any other in use in the database while ($check_prefix) { $avail = 'abcdefghijklmnopqrstuvwxyz0123456789'; //first character should be alpha $new_prefix = $avail[mt_rand(0, 25)]; //length of new prefix $prelength = mt_rand(4, 9); //generate remaning characters for ($i = 0; $i < $prelength; $i++) { $new_prefix .= $avail[mt_rand(0, 35)]; } //complete with underscore $new_prefix .= '_'; $new_prefix = esc_sql($new_prefix); //just be safe $check_prefix = $wpdb->get_results('SHOW TABLES LIKE "' . $new_prefix . '%";', ARRAY_N); //if there are no tables with that prefix in the database set checkPrefix to false } //assume this will work $type = 'updated'; $message = __('Settings Updated', 'better-wp-security'); $tables = $wpdb->get_results('SHOW TABLES LIKE "' . $wpdb->base_prefix . '%"', ARRAY_N); //retrieve a list of all tables in the DB //Rename each table foreach ($tables as $table) { $table = substr($table[0], strlen($wpdb->base_prefix), strlen($table[0])); //Get the table name without the old prefix //rename the table and generate an error if there is a problem if ($wpdb->query('RENAME TABLE `' . $wpdb->base_prefix . $table . '` TO `' . $new_prefix . $table . '`;') === false) { $type = 'error'; $message = sprintf('%s %s%s. %s', __('Error: Could not rename table', 'better-wp-security'), $wpdb->base_prefix, $table, __('You may have to rename the table manually.', 'better-wp-security')); add_settings_error('itsec', esc_attr('settings_updated'), $message, $type); } } if (is_multisite()) { //multisite requires us to rename each blogs' options $blogs = $wpdb->get_col("SELECT blog_id FROM `" . $new_prefix . "blogs` WHERE public = '1' AND archived = '0' AND mature = '0' AND spam = '0' ORDER BY blog_id DESC"); //get list of blog id's if (is_array($blogs)) { //make sure there are other blogs to update //update each blog's user_roles option foreach ($blogs as $blog) { $wpdb->query('UPDATE `' . $new_prefix . $blog . '_options` SET option_name = "' . $new_prefix . $blog . '_user_roles" WHERE option_name = "' . $wpdb->base_prefix . $blog . '_user_roles" LIMIT 1;'); } } } $upOpts = $wpdb->query('UPDATE `' . $new_prefix . 'options` SET option_name = "' . $new_prefix . 'user_roles" WHERE option_name = "' . $wpdb->base_prefix . 'user_roles" LIMIT 1;'); //update options table and set flag to false if there's an error if ($upOpts === false) { //set an error $type = 'error'; $message = __('Could not update prefix references in options table.', 'better-wp-security'); add_settings_error('itsec', esc_attr('settings_updated'), $message, $type); } $rows = $wpdb->get_results('SELECT * FROM `' . $new_prefix . 'usermeta`'); //get all rows in usermeta //update all prefixes in usermeta foreach ($rows as $row) { if (substr($row->meta_key, 0, strlen($wpdb->base_prefix)) == $wpdb->base_prefix) { $pos = $new_prefix . substr($row->meta_key, strlen($wpdb->base_prefix), strlen($row->meta_key)); $result = $wpdb->query('UPDATE `' . $new_prefix . 'usermeta` SET meta_key="' . $pos . '" WHERE meta_key= "' . $row->meta_key . '" LIMIT 1;'); if ($result == false) { $type = 'error'; $message = __('Could not update prefix references in usermeta table.', 'better-wp-security'); add_settings_error('itsec', esc_attr('settings_updated'), $message, $type); } } } require_once trailingslashit($GLOBALS['itsec_globals']['plugin_dir']) . 'core/lib/class-itsec-lib-config-file.php'; require_once trailingslashit($GLOBALS['itsec_globals']['plugin_dir']) . 'core/lib/class-itsec-lib-file.php'; $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path(); $config = ITSEC_Lib_File::read($config_file_path); $error = ''; if (is_wp_error($config)) { $error = sprintf(__('Unable to read the <code>wp-config.php</code> file in order to update the Database Prefix. Error details as follows: %1$s (%2$s)', 'better-wp-security'), $config->get_error_message(), $config->get_error_code()); } else { $regex = '/(\\$table_prefix\\s*=\\s*)([\'"]).+?\\2(\\s*;)/'; $config = preg_replace($regex, "\${1}'{$new_prefix}'\${3}", $config); $write_result = ITSEC_Lib_File::write($config_file_path, $config); if (is_wp_error($write_result)) { $error = sprintf(__('Unable to update the <code>wp-config.php</code> file in order to update the Database Prefix. Error details as follows: %1$s (%2$s)', 'better-wp-security'), $config->get_error_message(), $config->get_error_code()); } } if (!empty($error)) { add_settings_error('itsec', esc_attr('settings_updated'), $error, 'error'); add_site_option('itsec_manual_update', true); } $this->settings = $new_prefix; //this tells the form field that all went well. if (is_multisite()) { if (!empty($error)) { $error_handler = new WP_Error(); $error_handler->add('error', $error); $this->core->show_network_admin_notice($error_handler); } else { $this->core->show_network_admin_notice(false); } $this->settings = false; } }
/** * Replace matched content in a file. * * @since 1.17.0 * * @param string $file Config file to update. * @param array|string $replacements An array of regular expression string indexes with replacement string values. * @return int|WP_Error Number of replacements made or a WP_Error object on error. */ protected static function replace($file, $replacements) { $contents = self::get_file_contents($file); if (is_wp_error($contents)) { return $contents; } $total = 0; foreach ((array) $replacements as $pattern => $replacement) { $contents = preg_replace($pattern, $replacement, $contents, -1, $count); $total += $count; } // Write the new contents to the file and return the results. return ITSEC_Lib_File::write($file, $contents); }
/** * Sanitize and validate input * * @since 4.6.0 */ public function process_salts() { global $itsec_globals; require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' ); require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-file.php' ); $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path(); $config = ITSEC_Lib_File::read( $config_file_path ); $error = ''; if ( is_wp_error( $config ) ) { $error = sprintf( __( 'Unable to read the <code>wp-config.php</code> file in order to update the salts. Error details as follows: %1$s (%2$s)', 'it-l10n-ithemes-security-pro' ), $config->get_error_message(), $config->get_error_code() ); } else { $defines = array( 'AUTH_KEY', 'SECURE_AUTH_KEY', 'LOGGED_IN_KEY', 'NONCE_KEY', 'AUTH_SALT', 'SECURE_AUTH_SALT', 'LOGGED_IN_SALT', 'NONCE_SALT', ); foreach ( $defines as $define ) { $new_salt = $this->get_salt(); $new_salt = str_replace( '$', '\\$', $new_salt ); $regex = "/(define\s*\(\s*(['\"])$define\\2\s*,\s*)(['\"]).+?\\3(\s*\)\s*;)/"; $config = preg_replace( $regex, "\${1}'$new_salt'\${4}", $config ); } $write_result = ITSEC_Lib_File::write( $config_file_path, $config ); if ( is_wp_error( $write_result ) ) { $error = sprintf( __( 'Unable to update the <code>wp-config.php</code> file in order to update the salts. Error details as follows: %1$s (%2$s)', 'it-l10n-ithemes-security-pro' ), $config->get_error_message(), $config->get_error_code() ); } } if ( ! empty( $error ) ) { add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $error, 'error' ); add_site_option( 'itsec_manual_update', true ); } $this->settings = true; //this tells the form field that all went well. if ( is_multisite() ) { if ( ! empty( $error ) ) { $error_handler = new WP_Error(); $error_handler->add( 'error', $error ); $this->core->show_network_admin_notice( $error_handler ); } else { $this->core->show_network_admin_notice( false ); } $this->settings = true; } if ( $this->settings === true ) { update_site_option( 'itsec_salts', $itsec_globals['current_time_gmt'] ); wp_clear_auth_cookie(); $redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : ITSEC_Lib::get_home_root() . 'wp-login.php?loggedout=true'; wp_safe_redirect( $redirect_to ); } }
/** * Export all plugin settings and push to user. * * @since 4.5 * * @return mixed file or false */ private function export_settings() { global $wpdb, $itsec_globals; $email = trim( $_POST['email_address'] ); if ( ! is_email( $email ) ) { $message = sprintf( __( 'The supplied email address <code>%s</code> is not a valid email address. Settings were not exported. Please supply a valid email address and try again.', 'it-l10n-ithemes-security-pro' ), $email ); add_settings_error( 'itsec', 'settings_updated', $message, 'error' ); return; } $ignored_settings = array( //Array of settings that should not be exported 'itsec_local_file_list', 'itsec_jquery_version', 'itsec_initials', 'itsec_data', ); $raw_items = $wpdb->get_results( "SELECT * FROM `" . $wpdb->options . "` WHERE `option_name` LIKE 'itsec%';", ARRAY_A ); $clean_items = array(); //Loop through raw options to make sure serialized data is output as a JSON array (don't want to have to unserialize anything from the user later). foreach ( $raw_items as $item ) { if ( ! in_array( $item['option_name'], $ignored_settings ) ) { $clean_items[] = array( 'name' => $item['option_name'], 'value' => maybe_unserialize( $item['option_value'] ), 'auto' => ( $item['autoload'] === 'yes' ? 'yes' : 'no' ), ); } } $content = json_encode( $clean_items ); //encode the PHP array of settings $settings_file = '/itsec_options.json'; $zip_file = '/itsec_options.zip'; require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-file.php' ); $result = ITSEC_Lib_File::write( $itsec_globals['ithemes_dir'] . $settings_file, $content ); if ( is_wp_error( $result ) ) { $message = sprintf( __( 'Unable to create the backup file. %1$s (code: %2$s)', 'it-l10n-ithemes-security-pro' ), $result->get_error_message(), $result->get_error_code() ); add_settings_error( 'itsec', 'settings_updated', $message, 'error' ); return; } //Attempt to zip the saved file if ( ! class_exists( 'PclZip' ) ) { require( ABSPATH . 'wp-admin/includes/class-pclzip.php' ); } @chdir( $itsec_globals['ithemes_dir'] ); $zip = new PclZip( '.' . $zip_file ); $result = $zip->create( '.' . $settings_file ); @unlink( '.' . $settings_file ); if ( 0 === $result ) { $message = sprintf( __( 'Unable to create the zipped backup file: <code>%s</code>. An unknown error prevented the PclZip library from successfully creating the zip file.', 'it-l10n-ithemes-security-pro' ), "{$itsec_globals['ithemes_dir']}$zip_file" ); add_settings_error( 'itsec', 'settings_updated', $message, 'error' ); return; } $attachment = array( '.' . $zip_file ); $body = __( 'Attached is the settings file for ', 'it-l10n-ithemes-security-pro' ) . ' ' . get_option( 'siteurl' ) . __( ' created at', 'it-l10n-ithemes-security-pro' ) . ' ' . date( 'l, F jS, Y \a\\t g:i a', $itsec_globals['current_time'] ); //Setup the remainder of the email $subject = __( 'Security Settings File', 'it-l10n-ithemes-security-pro' ) . ' ' . date( 'l, F jS, Y \a\\t g:i a', $itsec_globals['current_time'] ); $subject = apply_filters( 'itsec_backup_email_subject', $subject ); $headers = 'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>' . "\r\n"; //Use HTML Content type add_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) ); if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG === true ) { $body .= '<p>' . __( 'Debug info (source page): ' . esc_url( $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ) ) . '</p>'; } $mail_success = wp_mail( $email, $subject, '<html>' . $body . '</html>', $headers, $attachment ); //Remove HTML Content type remove_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) ); if ( false === $mail_success ) { $message = __( 'We could not send the email. You will need to retrieve the backup file manually.', 'it-l10n-ithemes-security-pro' ); add_settings_error( 'itsec', 'settings_updated', $message, 'error' ); return; } @unlink( '.' . $zip_file ); add_settings_error( 'itsec', 'export_successful', sprintf( __( 'The export was created successfully. Please check %s for the export file.', 'it-l10n-ithemes-security-pro' ), $email ), 'updated' ); }
protected final function sanitize_setting($type, $var, $name, $prevent_save_on_error = true, $trim_value = true) { $id = $this->get_id(); if (!isset($this->settings[$var])) { $this->add_error(new WP_Error("itsec-validator-missing-var-{$id}-{$var}", sprintf(__('A validation check for %1$s failed. The %2$s value is missing. This could be due to a problem with the iThemes Security installation or an invalid modification. Please reinstall iThemes Security and try again.', 'better-wp-security'), $id, $name))); return false; } if ($trim_value && is_string($this->settings[$var])) { $this->settings[$var] = trim($this->settings[$var]); } $error = false; if ('string' === $type) { $this->settings[$var] = (string) $this->settings[$var]; } else { if ('non-empty-string' === $type) { $this->settings[$var] = (string) $this->settings[$var]; if (empty($this->settings[$var])) { $error = sprintf(__('The %1$s value cannot be empty.', 'better-wp-security'), $name); } } else { if ('title' === $type) { $this->settings[$var] = sanitize_title($this->settings[$var]); } else { if ('non-empty-title' === $type) { $this->settings[$var] = sanitize_title($this->settings[$var]); if (empty($this->settings[$var])) { $error = sprintf(__('The %1$s value cannot be empty.', 'better-wp-security'), $name); } } else { if ('array' === $type) { if (!is_array($this->settings[$var])) { if (empty($this->settings[$var])) { $this->settings[$var] = array(); } else { $this->settings[$var] = array($this->settings[$var]); } } } else { if ('bool' === $type) { if ('false' === $this->settings[$var]) { $this->settings[$var] = false; } else { if ('true' === $this->settings[$var]) { $this->settings[$var] = true; } else { $this->settings[$var] = (bool) $this->settings[$var]; } } } else { if ('int' === $type) { $test_val = intval($this->settings[$var]); if ((string) $test_val === (string) $this->settings[$var]) { $this->settings[$var] = $test_val; } else { $error = sprintf(__('The %1$s value must be an integer.', 'better-wp-security'), $name); } } else { if ('positive-int' === $type) { $test_val = intval($this->settings[$var]); if ((string) $test_val === (string) $this->settings[$var] && $test_val >= 0) { $this->settings[$var] = $test_val; } else { $error = sprintf(__('The %1$s value must be a positive integer.', 'better-wp-security'), $name); } } else { if ('email' === $type) { $this->settings[$var] = sanitize_text_field($this->settings[$var]); if (empty($this->settings[$var]) || !is_email($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a valid email address.', 'better-wp-security'), $name); } } else { if ('valid-username' === $type) { $this->settings[$var] = sanitize_text_field($this->settings[$var]); if (!empty($this->settings[$var]) && !validate_username($this->settings[$var])) { $error = sprintf(__('The %1$s value is not a valid username.', 'better-wp-security'), $name); } } else { if ('date' === $type) { $val = $this->settings[$var]; $separator = '[\\-/\\. ]'; if (preg_match("|^(\\d\\d\\d\\d){$separator}(\\d\\d?){$separator}(\\d\\d?)\$|", $val, $match)) { $year = intval($match[1]); $month = intval($match[2]); $day = intval($match[3]); if (!checkdate($month, $day, $year)) { $error = sprintf(__('The %1$s value must be a valid date.', 'better-wp-security'), $name); } } else { $error = sprintf(__('The %1$s value must be a valid date in the format of YYYY-MM-DD.', 'better-wp-security'), $name); } } else { if ('writable-directory' === $type) { if (!is_string($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a string.', 'better-wp-security'), $name); } else { require_once ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-directory.php'; $this->settings[$var] = rtrim($this->settings[$var], DIRECTORY_SEPARATOR); if (!ITSEC_Lib_Directory::is_dir($this->settings[$var])) { $result = ITSEC_Lib_Directory::create($this->settings[$var]); if (is_wp_error($result)) { $error = sprintf(_x('The directory supplied in %1$s cannot be used as a valid directory. %2$s', '%1$s is the input name. %2$s is the error message.', 'better-wp-security'), $name, $result->get_error_message()); } } if (empty($error) && !ITSEC_Lib_Directory::is_writable($this->settings[$var])) { $error = sprintf(__('The directory supplied in %1$s is not writable. Please select a directory that can be written to.', 'better-wp-security'), $name); } if (empty($error)) { ITSEC_Lib_Directory::add_file_listing_protection($this->settings[$var]); } } } else { if ('writable-file' === $type) { if (!is_string($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a string.', 'better-wp-security'), $name); } else { require_once ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-directory.php'; if (!ITSEC_Lib_File::is_file($this->settings[$var]) && ITSEC_Lib_File::exists($this->settings[$var])) { $error = sprintf(__('The file path supplied in %1$s cannot be used as it already exists but is not a file. Please supply a valid file path.', 'better-wp-security'), $name); } else { $result = ITSEC_Lib_Directory::create(dirname($this->settings[$var])); if (is_wp_error($result)) { $error = sprintf(_x('The file path supplied in %1$s cannot be used as the parent directory cannot be created. %2$s', '%1$s is the input name. %2$s is the error message.', 'better-wp-security'), $name, $result->get_error_message()); } else { if (!ITSEC_Lib_File::exists($this->settings[$var])) { $result = ITSEC_Lib_File::write($this->settings[$var], ''); if (is_wp_error($result)) { $error = sprintf(__('The file path supplied in %1$s could not be created. Please supply a file path that can be written to.', 'better-wp-security'), $name); } else { if (!is_writable($this->settings[$var])) { $error = sprintf(__('The file path supplied in %1$s was successfully created, but it cannot be updated. Please supply a file path that can be written to.', 'better-wp-security'), $name); } } } else { if (!is_writable($this->settings[$var])) { $error = sprintf(__('The file path supplied in %1$s is not writable. Please supply a file path that can be written to.', 'better-wp-security'), $name); } } } } } } else { if (is_array($type) && 2 === count($type) && $this === $type[0]) { $this->settings[$var] = $this->convert_string_to_array($this->settings[$var]); if (!is_array($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security'), $name); } else { $invalid_entries = array(); foreach ($this->settings[$var] as $index => $entry) { $entry = sanitize_text_field(trim($entry)); $this->settings[$var][$index] = $entry; if (empty($entry)) { unset($this->settings[$var][$index]); } else { $result = call_user_func($type, $entry); if (false === $result) { $invalid_entries[] = $entry; } else { $this->settings[$var][$index] = $result; } } } $this->settings[$var] = array_unique($this->settings[$var]); if (!empty($invalid_entries)) { $error = wp_sprintf(_n('The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count($invalid_entries), 'better-wp-security'), $name, $invalid_entries); } } } else { if (is_array($type)) { if (is_array($this->settings[$var])) { $invalid_entries = array(); foreach ($this->settings[$var] as $index => $entry) { $entry = sanitize_text_field(trim($entry)); $this->settings[$var][$index] = $entry; if (empty($entry)) { unset($this->settings[$var][$index]); } else { if (!in_array($entry, $type, true)) { $invalid_entries[] = $entry; } } } $this->settings[$var] = array_unique($this->settings[$var]); if (!empty($invalid_entries)) { $error = wp_sprintf(_n('The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count($invalid_entries), 'better-wp-security'), $name, $invalid_entries); } } else { if (!in_array($this->settings[$var], $type, true)) { $error = wp_sprintf(_n('The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count($type), 'better-wp-security'), $name, $type); $type = 'array'; } } } else { if ('newline-separated-array' === $type) { $this->settings[$var] = $this->convert_string_to_array($this->settings[$var]); if (!is_array($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security'), $name); } } else { if ('newline-separated-emails' === $type) { $this->settings[$var] = $this->convert_string_to_array($this->settings[$var]); if (!is_array($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security'), $name); } else { $invalid_emails = array(); foreach ($this->settings[$var] as $index => $email) { $email = sanitize_text_field(trim($email)); $this->settings[$var][$index] = $email; if (empty($email)) { unset($this->settings[$var][$index]); } else { if (!is_email($email)) { $invalid_emails[] = $email; } } } $this->settings[$var] = array_unique($this->settings[$var]); if (!empty($invalid_emails)) { $error = wp_sprintf(_n('The following email in %1$s is invalid: %2$l', 'The following emails in %1$s are invalid: %2$l', count($invalid_emails), 'better-wp-security'), $name, $invalid_emails); } } } else { if ('newline-separated-ips' === $type) { $this->settings[$var] = $this->convert_string_to_array($this->settings[$var]); if (!is_array($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security'), $name); } else { require_once ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-ip-tools.php'; $invalid_ips = array(); foreach ($this->settings[$var] as $index => $ip) { $ip = trim($ip); if ('' === $ip) { unset($this->settings[$var][$index]); } else { $validated_ip = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr($ip); if (false === $validated_ip) { $invalid_ips[] = $ip; } else { $this->settings[$var][$index] = $validated_ip; } } } $this->settings[$var] = array_unique($this->settings[$var]); if (!empty($invalid_ips)) { $error = wp_sprintf(_n('The following IP in %1$s is invalid: %2$l', 'The following IPs in %1$s are invalid: %2$l', count($invalid_ips), 'better-wp-security'), $name, $invalid_ips); } } } else { if ('newline-separated-extensions' === $type) { $this->settings[$var] = $this->convert_string_to_array($this->settings[$var]); if (!is_array($this->settings[$var])) { $error = sprintf(__('The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security'), $name); } else { $invalid_extensions = array(); foreach ($this->settings[$var] as $index => $extension) { if (!preg_match('/^(\\.[^.]+)+$/', $extension)) { $invalid_extensions[] = $extension; } } $this->settings[$var] = array_unique($this->settings[$var]); if (!empty($invalid_extensions)) { $error = wp_sprintf(_n('The following extension in %1$s is invalid: %2$l', 'The following extensions in %1$s are invalid: %2$l', count($invalid_extensions), 'better-wp-security'), $name, $invalid_extensions); } } } else { /* translators: 1: sanitize type, 2: input name */ $error = sprintf(__('An invalid sanitize type of "%1$s" was received for the %2$s input.', 'better-wp-security'), $type, $name); } } } } } } } } } } } } } } } } } } } if (false !== $error) { $this->add_error(new WP_Error("itsec-validator-{$id}-invalid-type-{$var}-{$type}", $error)); $this->vars_to_skip_validate_matching_types[] = $var; if ($prevent_save_on_error) { $this->set_can_save(false); } return false; } return true; }
/** * Add an index.php file to the directory to prevent file listing. * * @since 2.3.0 * * @param string $dir Full path to the directory to protect. * @return bool|WP_Error Boolean true if the file could be created or already exists, WP_Error object otherwise. */ public static function add_file_listing_protection($dir) { $dir = rtrim($dir, '/'); if (!self::is_dir($dir)) { return new WP_Error('itsec-lib-directory-add-file-listing-protection-directory-does-not-exist', sprintf(__('The directory %s could not be protected from file listing as the directory does not exist.', 'better-wp-security'), $dir)); } if (ITSEC_Lib_File::exists("{$dir}/index.php")) { return true; } return ITSEC_Lib_File::write("{$dir}/index.php", "<?php\n// Silence is golden."); }
/** * Update modifications in the supplied configuration file. * * If a blank $contents argument is supplied, all modifications will be removed. * * @since 1.15.0 * @access protected * * @param string $file Config file to update. * @param string $type The type of config file. Valid options are apache, nginx, and * wp-config. * @param string $modification The contents to add or update the file with. If an empty string is * supplied, all iThemes Security modifications will be removed. * @param bool $clear_existing_modifications Optional. Whether or not existing modifications should be removed * first. Defaults to true. * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise. */ protected static function update($file, $type, $modification, $clear_existing_modifications = true) { // Check to make sure that the settings give permission to write files. if (false === apply_filters('itsec_filter_can_write_to_files', false)) { $display_file = str_replace('\\', '/', $file); $abspath = str_replace('\\', '/', ABSPATH); $display_file = preg_replace('/^' . preg_quote($abspath, '/') . '/', '', $display_file); $display_file = ltrim($display_file, '/'); return new WP_Error('itsec-file-writes-are-disabled', sprintf(__('The "Write to Files" setting is disabled. Manual configuration for the <code>%s</code> file can be found on the Security > Dashboard page.', 'it-l10n-better-wp-security'), $display_file)); } if ($clear_existing_modifications) { $contents = self::get_file_contents_without_modification($file, $type); } else { $contents = self::get_file_contents($file); } if (is_wp_error($contents)) { return $contents; } $modification = ltrim($modification, "\v\r\n"); $modification = rtrim($modification, " \t\v\r\n"); if (empty($modification)) { // If there isn't a new modification, write the content without any modification and return the result. if (empty($contents)) { $contents = PHP_EOL; } return ITSEC_Lib_File::write($file, $contents); } $placeholder = self::get_placeholder(); // Ensure that the generated placeholder can be uniquely identified in the contents. while (false !== strpos($contents, $placeholder)) { $placeholder = self::get_placeholder(); } if ('wp-config' === $type) { // Put the placeholder at the beginning of the file, after the <?php tag. $contents = preg_replace('/^(.*?<\\?(?:php)?)\\s*(?:\\r\\r\\n|\\r\\n|\\r|\\n)/', "\${1}{$placeholder}", $contents, 1); if (false === strpos($contents, $placeholder)) { $contents = preg_replace('/^(.*?<\\?(?:php)?)\\s*(.+(?:\\r\\r\\n|\\r\\n|\\r|\\n))/', "\${1}{$placeholder}\$2", $contents, 1); } if (false === strpos($contents, $placeholder)) { $contents = "<?php{$placeholder}?" . ">{$contents}"; } } else { // Apache and nginx server config files. $contents = "{$placeholder}{$contents}"; } // Pad away from existing sections when adding iThemes Security modifications. $line_ending = self::get_line_ending($contents); while (!preg_match("/(?:^|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)){$placeholder}/", $contents)) { $contents = preg_replace("/{$placeholder}/", "{$line_ending}{$placeholder}", $contents); } while (!preg_match("/{$placeholder}(?:\$|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))/", $contents)) { $contents = preg_replace("/{$placeholder}/", "{$placeholder}{$line_ending}", $contents); } // Ensure that the file ends in a newline if the placeholder is at the end. $contents = preg_replace("/{$placeholder}\$/", "{$placeholder}{$line_ending}", $contents); if (!empty($modification)) { // Normalize line endings of the modification to match the file's line endings. $modification = ITSEC_Lib_Utility::normalize_line_endings($modification, $line_ending); // Exchange the placeholder with the modification. $contents = preg_replace("/{$placeholder}/", $modification, $contents); } // Write the new contents to the file and return the results. return ITSEC_Lib_File::write($file, $contents); }
public static function change_database_prefix() { global $wpdb; require_once $GLOBALS['itsec_globals']['plugin_dir'] . 'core/lib/class-itsec-lib-config-file.php'; require_once $GLOBALS['itsec_globals']['plugin_dir'] . 'core/lib/class-itsec-lib-file.php'; $response = array('errors' => array(), 'new_prefix' => false); //suppress error messages due to timing // error_reporting( 0 ); // @ini_set( 'display_errors', 0 ); $check_prefix = true; //Assume the first prefix we generate is unique //generate a new table prefix that doesn't conflict with any other in use in the database while ($check_prefix) { $avail = 'abcdefghijklmnopqrstuvwxyz0123456789'; //first character should be alpha $new_prefix = $avail[mt_rand(0, 25)]; //length of new prefix $prelength = mt_rand(4, 9); //generate remaning characters for ($i = 0; $i < $prelength; $i++) { $new_prefix .= $avail[mt_rand(0, 35)]; } //complete with underscore $new_prefix .= '_'; $new_prefix = esc_sql($new_prefix); //just be safe $check_prefix = $wpdb->get_results('SHOW TABLES LIKE "' . $new_prefix . '%";', ARRAY_N); //if there are no tables with that prefix in the database set checkPrefix to false } $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path(); $config = ITSEC_Lib_File::read($config_file_path); if (is_wp_error($config)) { /* translators: 1: Specific error details */ $response['errors'][] = new WP_Error($confix->get_error_code(), sprintf(__('Unable to read the <code>wp-config.php</code> file in order to update the Database Prefix. Error details as follows: %1$s', 'better-wp-security'), $config->get_error_message())); return $response; } $regex = '/(\\$table_prefix\\s*=\\s*)([\'"]).+?\\2(\\s*;)/'; $config = preg_replace($regex, "\${1}'{$new_prefix}'\${3}", $config); $write_result = ITSEC_Lib_File::write($config_file_path, $config); if (is_wp_error($write_result)) { /* translators: 1: Specific error details */ $response['errors'][] = new WP_Error($confix->get_error_code(), sprintf(__('Unable to update the <code>wp-config.php</code> file in order to update the Database Prefix. Error details as follows: %1$s', 'better-wp-security'), $config->get_error_message())); return $response; } $response['new_prefix'] = $new_prefix; $tables = $wpdb->get_results('SHOW TABLES LIKE "' . $wpdb->base_prefix . '%"', ARRAY_N); //retrieve a list of all tables in the DB //Rename each table foreach ($tables as $table) { $table = substr($table[0], strlen($wpdb->base_prefix), strlen($table[0])); //Get the table name without the old prefix //rename the table and generate an error if there is a problem if ($wpdb->query('RENAME TABLE `' . $wpdb->base_prefix . $table . '` TO `' . $new_prefix . $table . '`;') === false) { $response['errors'][] = new WP_Error('itsec-database-prefix-utility-change-database-prefix-failed-table-rename', sprintf(__('Could not rename table %1$s. You may have to rename the table manually.', 'better-wp-security'), $wpdb->base_prefix . $table)); } } if (is_multisite()) { //multisite requires us to rename each blogs' options $blogs = $wpdb->get_col("SELECT blog_id FROM `" . $new_prefix . "blogs` WHERE public = '1' AND archived = '0' AND mature = '0' AND spam = '0' ORDER BY blog_id DESC"); //get list of blog id's if (is_array($blogs)) { //make sure there are other blogs to update //update each blog's user_roles option foreach ($blogs as $blog) { $wpdb->query('UPDATE `' . $new_prefix . $blog . '_options` SET option_name = "' . $new_prefix . $blog . '_user_roles" WHERE option_name = "' . $wpdb->base_prefix . $blog . '_user_roles" LIMIT 1;'); } } } $upOpts = $wpdb->query('UPDATE `' . $new_prefix . 'options` SET option_name = "' . $new_prefix . 'user_roles" WHERE option_name = "' . $wpdb->base_prefix . 'user_roles" LIMIT 1;'); //update options table and set flag to false if there's an error if ($upOpts === false) { //set an error $response['errors'][] = new WP_Error('itsec-database-prefix-utility-change-database-prefix-failed-options-update', __('Could not update prefix references in options table.', 'better-wp-security')); } $rows = $wpdb->get_results('SELECT * FROM `' . $new_prefix . 'usermeta`'); //get all rows in usermeta //update all prefixes in usermeta foreach ($rows as $row) { if (substr($row->meta_key, 0, strlen($wpdb->base_prefix)) == $wpdb->base_prefix) { $pos = $new_prefix . substr($row->meta_key, strlen($wpdb->base_prefix), strlen($row->meta_key)); $result = $wpdb->query('UPDATE `' . $new_prefix . 'usermeta` SET meta_key="' . $pos . '" WHERE meta_key= "' . $row->meta_key . '" LIMIT 1;'); if ($result == false) { $response['errors'][] = new WP_Error('itsec-database-prefix-utility-change-database-prefix-failed-usermeta-update', __('Could not update prefix references in usermeta table.', 'better-wp-security')); } } } return $response; }