727 lines
27 KiB
PHP
727 lines
27 KiB
PHP
<?php
|
||
if (!defined('ABSPATH')) {
|
||
exit;//Exit if accessed directly
|
||
}
|
||
|
||
class AIOWPSecurity_Utility_Htaccess {
|
||
|
||
// The following variables will store the comment markers for each of features added to the .htacces file
|
||
// This will make it easy to locate the blocks of code for deletion if someone disables a feature
|
||
public static $ip_blacklist_marker_start = '#AIOWPS_IP_BLACKLIST_START';
|
||
|
||
public static $ip_blacklist_marker_end = '#AIOWPS_IP_BLACKLIST_END';
|
||
|
||
public static $prevent_wp_file_access_marker_start = '#AIOWPS_BLOCK_WP_FILE_ACCESS_START';
|
||
|
||
public static $prevent_wp_file_access_marker_end = '#AIOWPS_BLOCK_WP_FILE_ACCESS_END';
|
||
|
||
public static $basic_htaccess_rules_marker_start = '#AIOWPS_BASIC_HTACCESS_RULES_START';
|
||
|
||
public static $basic_htaccess_rules_marker_end = '#AIOWPS_BASIC_HTACCESS_RULES_END';
|
||
|
||
public static $debug_log_block_htaccess_rules_marker_start = '#AIOWPS_DEBUG_LOG_BLOCK_HTACCESS_RULES_START';
|
||
|
||
public static $debug_log_block_htaccess_rules_marker_end = '#AIOWPS_DEBUG_LOG_BLOCK_HTACCESS_RULES_END';
|
||
|
||
public static $user_agent_blacklist_marker_start = '#AIOWPS_USER_AGENT_BLACKLIST_START';
|
||
|
||
public static $user_agent_blacklist_marker_end = '#AIOWPS_USER_AGENT_BLACKLIST_END';
|
||
|
||
public static $enable_brute_force_attack_prevention_marker_start = '#AIOWPS_ENABLE_BRUTE_FORCE_PREVENTION_START';
|
||
|
||
public static $enable_brute_force_attack_prevention_marker_end = '#AIOWPS_ENABLE_BRUTE_FORCE_PREVENTION_END';
|
||
|
||
public static $disable_index_views_marker_start = '#AIOWPS_DISABLE_INDEX_VIEWS_START';
|
||
|
||
public static $disable_index_views_marker_end = '#AIOWPS_DISABLE_INDEX_VIEWS_END';
|
||
|
||
public static $disable_trace_track_marker_start = '#AIOWPS_DISABLE_TRACE_TRACK_START';
|
||
|
||
public static $disable_trace_track_marker_end = '#AIOWPS_DISABLE_TRACE_TRACK_END';
|
||
|
||
public static $five_g_blacklist_marker_start = '#AIOWPS_FIVE_G_BLACKLIST_START';
|
||
|
||
public static $five_g_blacklist_marker_end = '#AIOWPS_FIVE_G_BLACKLIST_END';
|
||
|
||
public static $six_g_blacklist_marker_start = '#AIOWPS_SIX_G_BLACKLIST_START';
|
||
|
||
public static $six_g_blacklist_marker_end = '#AIOWPS_SIX_G_BLACKLIST_END';
|
||
|
||
public static $block_spambots_marker_start = '#AIOWPS_BLOCK_SPAMBOTS_START';
|
||
|
||
public static $block_spambots_marker_end = '#AIOWPS_BLOCK_SPAMBOTS_END';
|
||
|
||
public static $enable_login_whitelist_marker_start = '#AIOWPS_LOGIN_WHITELIST_START';
|
||
|
||
public static $enable_login_whitelist_marker_end = '#AIOWPS_LOGIN_WHITELIST_END';
|
||
|
||
public static $prevent_image_hotlinks_marker_start = '#AIOWPS_PREVENT_IMAGE_HOTLINKS_START';
|
||
|
||
public static $prevent_image_hotlinks_marker_end = '#AIOWPS_PREVENT_IMAGE_HOTLINKS_END';
|
||
|
||
public static $custom_rules_marker_start = '#AIOWPS_CUSTOM_RULES_START';
|
||
|
||
public static $custom_rules_marker_end = '#AIOWPS_CUSTOM_RULES_END';
|
||
|
||
/**
|
||
* TODO - enter more markers as new .htaccess features are added
|
||
*/
|
||
public function __construct() {
|
||
//NOP
|
||
}
|
||
|
||
/**
|
||
* This function is used to scan the htaccess file for valid markers
|
||
*
|
||
* @param string $htaccess - The htaccess file to scan
|
||
* @return boolean
|
||
*/
|
||
public static function htaccess_has_valid_markers($htaccess) {
|
||
|
||
$markers = array(
|
||
'# BEGIN WordPress',
|
||
'# END WordPress',
|
||
'# BEGIN All In One WP Security',
|
||
'# END All In One WP Security',
|
||
self::$ip_blacklist_marker_start,
|
||
self::$ip_blacklist_marker_end,
|
||
self::$prevent_wp_file_access_marker_start,
|
||
self::$prevent_wp_file_access_marker_end,
|
||
self::$basic_htaccess_rules_marker_start,
|
||
self::$basic_htaccess_rules_marker_end,
|
||
self::$debug_log_block_htaccess_rules_marker_start,
|
||
self::$debug_log_block_htaccess_rules_marker_end,
|
||
self::$user_agent_blacklist_marker_start,
|
||
self::$user_agent_blacklist_marker_end,
|
||
self::$enable_brute_force_attack_prevention_marker_start,
|
||
self::$enable_brute_force_attack_prevention_marker_end,
|
||
self::$disable_index_views_marker_start,
|
||
self::$disable_index_views_marker_end,
|
||
self::$disable_trace_track_marker_start,
|
||
self::$disable_trace_track_marker_end,
|
||
self::$five_g_blacklist_marker_start,
|
||
self::$five_g_blacklist_marker_end,
|
||
self::$six_g_blacklist_marker_start,
|
||
self::$six_g_blacklist_marker_end,
|
||
self::$block_spambots_marker_start,
|
||
self::$block_spambots_marker_end,
|
||
self::$enable_login_whitelist_marker_start,
|
||
self::$enable_login_whitelist_marker_end,
|
||
self::$prevent_image_hotlinks_marker_start,
|
||
self::$prevent_image_hotlinks_marker_end,
|
||
self::$custom_rules_marker_start,
|
||
self::$custom_rules_marker_end,
|
||
);
|
||
|
||
$markerCounts = array();
|
||
$htaccessContent = file_get_contents($htaccess);
|
||
|
||
foreach ($markers as $marker) {
|
||
$count = preg_match_all('/^' . preg_quote($marker, '/') . '$/m', $htaccessContent);
|
||
$markerCounts[$marker] = $count;
|
||
}
|
||
|
||
foreach ($markerCounts as $count) {
|
||
if ($count > 1) return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* This function checks if .htaccess file exists and is readable
|
||
*
|
||
* @param string $htaccess - The path to .htaccess file
|
||
* @return boolean
|
||
*/
|
||
public static function htaccess_exist_and_readable($htaccess) {
|
||
global $aio_wp_security;
|
||
|
||
if (!file_exists($htaccess)) {
|
||
$aio_wp_security->debug_logger->log_debug("The .htaccess file is missing", 4);
|
||
return false;
|
||
} elseif (!is_readable($htaccess)) {
|
||
$aio_wp_security->debug_logger->log_debug("The .htaccess file exists, but it is not readable.", 4);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Write all active rules to .htaccess file.
|
||
*
|
||
* @param boolean $show_error - if the error should be shown
|
||
*
|
||
* @return boolean True on success, false on failure.
|
||
*/
|
||
public static function write_to_htaccess($show_error = true) {
|
||
global $aio_wp_security;
|
||
|
||
if (!class_exists('AIOWPSecurity_Admin_Menu')) {
|
||
include_once AIO_WP_SECURITY_PATH . '/admin/wp-security-admin-menu.php';
|
||
}
|
||
|
||
// figure out what server is being used
|
||
$serverType = AIOWPSecurity_Utility::get_server_type();
|
||
|
||
if (in_array($serverType, array('-1', 'nginx', 'iis')) && !defined('WP_CLI')) {
|
||
if ($show_error) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('The .htaccess file is not supported by your web server.', 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
}
|
||
$aio_wp_security->debug_logger->log_debug("Unable to write to .htaccess - server type not supported.", 4);
|
||
return false; // unable to write to the file
|
||
}
|
||
|
||
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
||
$htaccess = $home_path . '.htaccess';
|
||
if (!self::htaccess_exist_and_readable($htaccess)) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('The .htaccess file either does not exist or is unreadable', 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
$aio_wp_security->debug_logger->log_debug("The .htaccess file either does not exist or is unreadable", 4);
|
||
return false;
|
||
} // check the existence of the file and if its readable
|
||
|
||
// confirm the hataccess has valid markers
|
||
if (!self::htaccess_has_valid_markers($htaccess)) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('The .htaccess file contains invalid content, please manually verify the file contents', 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
$aio_wp_security->debug_logger->log_debug("Unable to edit the .htaccess file as it contains invalid content, please manually verify the file contents", 4);
|
||
return false;
|
||
}
|
||
|
||
AIOWPSecurity_Utility_File::backup_and_rename_htaccess($htaccess);
|
||
|
||
// creating a copy of htaccess file to work on
|
||
$temp_htaccess = $home_path.'.htaccess_temp';
|
||
if (!copy($htaccess, $temp_htaccess)) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('A copy of the .htaccess file could not be created', 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
$aio_wp_security->debug_logger->log_debug("Write operation on .htaccess file failed, unable to create a copy of the file", 4);
|
||
return false;
|
||
}
|
||
|
||
// clean up old rules first
|
||
if (-1 == AIOWPSecurity_Utility_Htaccess::delete_from_htaccess($temp_htaccess)) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__("Unable to delete plugin's content from .htaccess file.", 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
$aio_wp_security->debug_logger->log_debug("Unable to delete plugin's content from .htaccess file.", 4);
|
||
return false; //unable to write to the file
|
||
}
|
||
|
||
|
||
$ht = explode("\n", implode('', file($temp_htaccess))); // parse each line of file into array
|
||
|
||
$rules = AIOWPSecurity_Utility_Htaccess::getrules();
|
||
|
||
$rulesarray = explode("\n", $rules);
|
||
$rulesarray = apply_filters('aiowps_htaccess_rules_before_writing', $rulesarray);
|
||
$contents = array_merge($rulesarray, $ht);
|
||
|
||
$f = @fopen($temp_htaccess, 'w+'); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore warning as we try to handle it below
|
||
if (!$f) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('Write operation on .htaccess failed.', 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
$aio_wp_security->debug_logger->log_debug("Write operation on .htaccess failed.", 4);
|
||
return false; //we can't write to the file
|
||
}
|
||
|
||
$blank = false;
|
||
|
||
// write each line to file
|
||
foreach ($contents as $insertline) {
|
||
if (trim($insertline) == '') {
|
||
if (false == $blank) {
|
||
fwrite($f, "\n" . trim($insertline));
|
||
}
|
||
$blank = true;
|
||
} else {
|
||
$blank = false;
|
||
fwrite($f, "\n" . trim($insertline));
|
||
}
|
||
}
|
||
if (is_resource($f)) @fclose($f);
|
||
|
||
// before writing into the live htaccess confirm the markers still valid
|
||
if (!self::htaccess_has_valid_markers($temp_htaccess)) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('The .htaccess file has invalid content, please manually verify that the file is properly formatted', 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
$aio_wp_security->debug_logger->log_debug("The .htaccess file has invalid content, please manually verify that the file is properly formatted", 4);
|
||
return false;
|
||
}
|
||
|
||
// copy the changes from the temp htaccess into the live htaccess from here
|
||
if (!copy($temp_htaccess, $htaccess)) {
|
||
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('An error has occurred while writing to the .htaccess file.', 'all-in-one-wp-security-and-firewall'), !$show_error);
|
||
$aio_wp_security->debug_logger->log_debug("Failed to write to the .htaccess file", 4);
|
||
return false;
|
||
}
|
||
// Remove the temp htaccess file created
|
||
unlink($temp_htaccess);
|
||
|
||
return true; //success
|
||
}
|
||
|
||
/**
|
||
* This function will delete the code which has been added to the .htaccess file by this plugin
|
||
* It will try to find the comment markers "# BEGIN All In One WP Security" and "# END All In One WP Security" and delete contents in between
|
||
*
|
||
* @param string $htaccess - The htaccess file path to manipulate
|
||
* @param string $section - All in One Security
|
||
* @return Integer {-1,1} -1 for failure, 1 for success.
|
||
*/
|
||
public static function delete_from_htaccess($htaccess = '', $section = 'All In One WP Security') {
|
||
|
||
if (empty($htaccess)) {
|
||
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
||
$htaccess = $home_path . '.htaccess';
|
||
}
|
||
|
||
if (!file_exists($htaccess)) {
|
||
$ht = @fopen($htaccess, 'a+');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore warning as we try to handle it below
|
||
if (false === $ht) {
|
||
global $aio_wp_security;
|
||
$aio_wp_security->debug_logger->log_debug('Failed to create .htaccess file', 4);
|
||
return -1;
|
||
}
|
||
if (is_resource($ht)) @fclose($ht);
|
||
}
|
||
|
||
// Bug Fix: On some environments such as windows (xampp) this function was clobbering the non-aiowps-related .htaccess contents for certain cases.
|
||
// In some cases when WordPress saves the .htaccess file (eg, when saving permalink settings),
|
||
// the line endings differ from the expected "\n" endings. (WordPress saves with "\n" (UNIX style) but "\n" may be set as "\r\n" (WIN/DOS))
|
||
// In this case exploding via "\n" may not yield the result we expect.
|
||
// Therefore we need to do the following extra checks.
|
||
$ht_contents_imploded = implode('', file($htaccess));
|
||
if (empty($ht_contents_imploded)) {
|
||
return 1;
|
||
} elseif (strstr($ht_contents_imploded, "\n")) {
|
||
$ht_contents = explode("\n", $ht_contents_imploded); //parse each line of file into array
|
||
} elseif (strstr($ht_contents_imploded, "\r")) {
|
||
$ht_contents = explode("\r", $ht_contents_imploded); //parse each line of file into array
|
||
} elseif (strstr($ht_contents_imploded, "\r\n")) {
|
||
$ht_contents = explode("\r\n", $ht_contents_imploded); //parse each line of file into array
|
||
}
|
||
|
||
if ($ht_contents) { //as long as there are lines in the file
|
||
$state = true;
|
||
$f = @fopen($htaccess, 'w+'); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore warning as we try to handle it below
|
||
if (!$f) {
|
||
@chmod($htaccess, 0644);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore warning as we try to handle it below
|
||
$f = @fopen($htaccess, 'w+'); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore warning as we try to handle it below
|
||
if (!$f) return -1;
|
||
}
|
||
|
||
foreach ($ht_contents as $markerline) { //for each line in the file
|
||
if (strpos($markerline, '# BEGIN ' . $section) !== false) { //if we're at the beginning of the section
|
||
$state = false;
|
||
}
|
||
if (true == $state) { //as long as we're not in the section keep writing
|
||
fwrite($f, trim($markerline) . "\n");
|
||
}
|
||
if (strpos($markerline, '# END ' . $section) !== false) { //see if we're at the end of the section
|
||
$state = true;
|
||
}
|
||
}
|
||
if (is_resource($f)) @fclose($f);
|
||
return 1;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
public static function getrules() {
|
||
global $aio_wp_security;
|
||
$rules = "";
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_basic_htaccess();
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_block_debug_log_access_htaccess();
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_disable_index_views();
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_disable_trace_and_track();
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_5g_blacklist();
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::prevent_image_hotlinks();
|
||
$custom_rules = AIOWPSecurity_Utility_Htaccess::getrules_custom_rules();
|
||
if ($aio_wp_security->configs->get_value('aiowps_place_custom_rules_at_top')=='1') {
|
||
$rules = $custom_rules . $rules;
|
||
} else {
|
||
$rules .= $custom_rules;
|
||
}
|
||
|
||
//TODO: The following utility functions are ready to use when we write the menu pages for these features
|
||
|
||
//Add more functions for features as needed
|
||
//$rules .= AIOWPSecurity_Utility_Htaccess::getrules_somefeature();
|
||
|
||
//Add outer markers if we have rules
|
||
if ('' != $rules) {
|
||
$rules = "# BEGIN All In One WP Security" . "\n" . $rules . "# END All In One WP Security" . "\n";
|
||
}
|
||
|
||
return $rules;
|
||
}
|
||
|
||
/**
|
||
* TODO - info
|
||
*/
|
||
public static function getrules_basic_htaccess() {
|
||
global $aio_wp_security;
|
||
|
||
$rules = '';
|
||
if ($aio_wp_security->configs->get_value('aiowps_enable_basic_firewall') == '1') {
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$basic_htaccess_rules_marker_start . "\n"; //Add feature marker start
|
||
//protect the htaccess file - this is done by default with apache config file but we are including it here for good measure
|
||
$rules .= self::create_apache2_access_denied_rule('.htaccess');
|
||
|
||
//disable the server signature
|
||
$rules .= 'ServerSignature Off' . "\n";
|
||
|
||
//limit file upload size
|
||
$upload_limit = $aio_wp_security->configs->get_value('aiowps_max_file_upload_size');
|
||
//Shouldn't be empty but just in case
|
||
$upload_limit = empty($upload_limit) ? AIOS_FIREWALL_MAX_FILE_UPLOAD_LIMIT_MB : $upload_limit;
|
||
$upload_limit = $upload_limit * 1024 * 1024; // Convert from MB to Bytes - approx but close enough
|
||
|
||
$rules .= 'LimitRequestBody '.$upload_limit . "\n";
|
||
|
||
// protect wpconfig.php.
|
||
$rules .= self::create_apache2_access_denied_rule('wp-config.php');
|
||
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$basic_htaccess_rules_marker_end . "\n"; //Add feature marker end
|
||
}
|
||
return $rules;
|
||
}
|
||
|
||
public static function getrules_block_debug_log_access_htaccess() {
|
||
global $aio_wp_security;
|
||
|
||
$rules = '';
|
||
if ($aio_wp_security->configs->get_value('aiowps_block_debug_log_file_access') == '1') {
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$debug_log_block_htaccess_rules_marker_start . "\n"; //Add feature marker start
|
||
$rules .= self::create_apache2_access_denied_rule('debug.log');
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$debug_log_block_htaccess_rules_marker_end . "\n"; //Add feature marker end
|
||
}
|
||
return $rules;
|
||
}
|
||
|
||
/**
|
||
* This function will disable directory listings for all directories, add this line to the
|
||
* site’s root .htaccess file.
|
||
* NOTE: AllowOverride must be enabled in the httpd.conf file for this to work!
|
||
*/
|
||
public static function getrules_disable_index_views() {
|
||
global $aio_wp_security;
|
||
$rules = '';
|
||
if ($aio_wp_security->configs->get_value('aiowps_disable_index_views') == '1') {
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$disable_index_views_marker_start . "\n"; //Add feature marker start
|
||
$rules .= 'Options -Indexes' . "\n";
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$disable_index_views_marker_end . "\n"; //Add feature marker end
|
||
}
|
||
|
||
return $rules;
|
||
}
|
||
|
||
/**
|
||
* This function will write rules to disable trace and track.
|
||
* HTTP Trace attack (XST) can be used to return header requests
|
||
* and grab cookies and other information and is used along with
|
||
* a cross site scripting attacks (XSS)
|
||
*/
|
||
public static function getrules_disable_trace_and_track() {
|
||
global $aio_wp_security;
|
||
$rules = '';
|
||
if ($aio_wp_security->configs->get_value('aiowps_disable_trace_and_track') == '1') {
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$disable_trace_track_marker_start . "\n"; //Add feature marker start
|
||
$rules .= '<IfModule mod_rewrite.c>' . "\n";
|
||
$rules .= 'RewriteEngine On' . "\n";
|
||
$rules .= 'RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)' . "\n";
|
||
$rules .= 'RewriteRule .* - [F]' . "\n";
|
||
$rules .= '</IfModule>' . "\n";
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$disable_trace_track_marker_end . "\n"; //Add feature marker end
|
||
}
|
||
|
||
return $rules;
|
||
}
|
||
|
||
|
||
/**
|
||
* This function contains the rules for the 5G blacklist produced by Jeff Starr from perishablepress.com
|
||
* NOTE: Since Jeff regularly updates and evolves his blacklist rules, ie, 5G->6G->7G.... we will update this function to reflect the latest blacklist release
|
||
*/
|
||
public static function getrules_5g_blacklist() {
|
||
global $aio_wp_security;
|
||
$rules = '';
|
||
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_5g_firewall')) {
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$five_g_blacklist_marker_start . "\n"; //Add feature marker start
|
||
|
||
$rules .= '# 5G BLACKLIST/FIREWALL (2013)
|
||
# @ http://perishablepress.com/5g-blacklist-2013/
|
||
|
||
# 5G:[QUERY STRINGS]
|
||
<IfModule mod_rewrite.c>
|
||
RewriteEngine On
|
||
RewriteBase /
|
||
RewriteCond %{QUERY_STRING} (\"|%22).*(<|>|%3) [NC,OR]
|
||
RewriteCond %{QUERY_STRING} (javascript:).*(\;) [NC,OR]
|
||
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3) [NC,OR]
|
||
RewriteCond %{QUERY_STRING} (\\\|\.\./|`|=\'$|=%27$) [NC,OR]
|
||
RewriteCond %{QUERY_STRING} (\;|\'|\"|%22).*(union|select|insert|drop|update|md5|benchmark|or|and|if) [NC,OR]
|
||
RewriteCond %{QUERY_STRING} (base64_encode|localhost|mosconfig) [NC,OR]
|
||
RewriteCond %{QUERY_STRING} (boot\.ini|echo.*kae|etc/passwd) [NC,OR]
|
||
RewriteCond %{QUERY_STRING} (GLOBALS|REQUEST)(=|\[|%) [NC]
|
||
RewriteRule .* - [F]
|
||
</IfModule>
|
||
|
||
# 5G:[USER AGENTS]
|
||
<IfModule mod_setenvif.c>
|
||
# SetEnvIfNoCase User-Agent ^$ keep_out
|
||
SetEnvIfNoCase User-Agent (binlar|casper|cmsworldmap|comodo|diavol|dotbot|feedfinder|flicky|ia_archiver|jakarta|kmccrew|nutch|planetwork|purebot|pycurl|skygrid|sucker|turnit|vikspider|zmeu) keep_out
|
||
<limit GET POST PUT>
|
||
Order Allow,Deny
|
||
Allow from all
|
||
Deny from env=keep_out
|
||
</limit>
|
||
</IfModule>
|
||
|
||
# 5G:[REQUEST STRINGS]
|
||
<IfModule mod_alias.c>
|
||
RedirectMatch 403 (https?|ftp|php)\://
|
||
RedirectMatch 403 /(https?|ima|ucp)/
|
||
RedirectMatch 403 /(Permanent|Better)$
|
||
RedirectMatch 403 (\=\\\\\\\'|\=\\\%27|/\\\\\\\'/?|\)\.css\()$
|
||
RedirectMatch 403 (\,|\)\+|/\,/|\{0\}|\(/\(|\.\.\.|\+\+\+|\||\\\\\"\\\\\")
|
||
RedirectMatch 403 \.(cgi|asp|aspx|cfg|dll|exe|jsp|mdb|sql|ini|rar)$
|
||
RedirectMatch 403 /(contac|fpw|install|pingserver|register)\.php$
|
||
RedirectMatch 403 (base64|crossdomain|localhost|wwwroot|e107\_)
|
||
RedirectMatch 403 (eval\(|\_vti\_|\(null\)|echo.*kae|config\.xml)
|
||
RedirectMatch 403 \.well\-known/host\-meta
|
||
RedirectMatch 403 /function\.array\-rand
|
||
RedirectMatch 403 \)\;\$\(this\)\.html\(
|
||
RedirectMatch 403 proc/self/environ
|
||
RedirectMatch 403 msnbot\.htm\)\.\_
|
||
RedirectMatch 403 /ref\.outcontrol
|
||
RedirectMatch 403 com\_cropimage
|
||
RedirectMatch 403 indonesia\.htm
|
||
RedirectMatch 403 \{\$itemURL\}
|
||
RedirectMatch 403 function\(\)
|
||
RedirectMatch 403 labels\.rdf
|
||
RedirectMatch 403 /playing.php
|
||
RedirectMatch 403 muieblackcat
|
||
</IfModule>
|
||
|
||
# 5G:[REQUEST METHOD]
|
||
<ifModule mod_rewrite.c>
|
||
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
|
||
RewriteRule .* - [F]
|
||
</IfModule>' . "\n";
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$five_g_blacklist_marker_end . "\n"; //Add feature marker end
|
||
}
|
||
|
||
return $rules;
|
||
}
|
||
|
||
/**
|
||
* This function will write some directives to prevent image hotlinking
|
||
*/
|
||
public static function prevent_image_hotlinks() {
|
||
global $aio_wp_security;
|
||
$rules = '';
|
||
if ($aio_wp_security->configs->get_value('aiowps_prevent_hotlinking') == '1') {
|
||
$url_string = AIOWPSecurity_Utility_Htaccess::return_regularized_url(AIOWPSEC_WP_HOME_URL);
|
||
if (false == $url_string) {
|
||
$url_string = AIOWPSEC_WP_HOME_URL;
|
||
}
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$prevent_image_hotlinks_marker_start . "\n"; //Add feature marker start
|
||
$rules .= '<IfModule mod_rewrite.c>' . "\n";
|
||
$rules .= 'RewriteEngine On' . "\n";
|
||
$rules .= 'RewriteCond %{HTTP_REFERER} !^$' . "\n";
|
||
$rules .= 'RewriteCond %{REQUEST_FILENAME} -f' . "\n";
|
||
$rules .= 'RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g?|png)$ [NC]' . "\n";
|
||
$rules .= 'RewriteCond %{HTTP_REFERER} !^' . $url_string . ' [NC]' . "\n";
|
||
$rules .= 'RewriteRule \.(gif|jpe?g?|png)$ - [F,NC,L]' . "\n";
|
||
$rules .= '</IfModule>' . "\n";
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$prevent_image_hotlinks_marker_end . "\n"; //Add feature marker end
|
||
}
|
||
|
||
return $rules;
|
||
}
|
||
|
||
/**
|
||
* This function will write any custom htaccess rules into the server's .htaccess file
|
||
*
|
||
* @return string
|
||
*/
|
||
public static function getrules_custom_rules() {
|
||
global $aio_wp_security;
|
||
$rules = '';
|
||
if ($aio_wp_security->configs->get_value('aiowps_enable_custom_rules') == '1') {
|
||
$custom_rules = $aio_wp_security->configs->get_value('aiowps_custom_rules');
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$custom_rules_marker_start . "\n"; //Add feature marker start
|
||
$rules .= $custom_rules . "\n";
|
||
$rules .= AIOWPSecurity_Utility_Htaccess::$custom_rules_marker_end . "\n"; //Add feature marker end
|
||
}
|
||
|
||
return $rules;
|
||
}
|
||
|
||
/**
|
||
* This function will do a quick check to see if a file's contents are actually .htaccess specific.
|
||
* At the moment it will look for the following tag somewhere in the file - "# BEGIN WordPress"
|
||
* If it finds the tag it will deem the file as being .htaccess specific.
|
||
* This was written to supplement the .htaccess restore functionality
|
||
*
|
||
* @param string $file_contents - the contents of the .htaccess file
|
||
*
|
||
* @return boolean
|
||
*/
|
||
public static function check_if_htaccess_contents($file_contents) {
|
||
$is_htaccess = false;
|
||
|
||
if (false === $file_contents || strlen($file_contents) == 0) {
|
||
return -1;
|
||
}
|
||
|
||
if ((strpos($file_contents, '# BEGIN WordPress') !== false) || (strpos($file_contents, '# BEGIN') !== false)) {
|
||
$is_htaccess = true; // It appears that we have some sort of .htaccess file
|
||
} else {
|
||
//see if we're at the end of the section
|
||
$is_htaccess = false;
|
||
}
|
||
|
||
if ($is_htaccess) {
|
||
return 1;
|
||
} else {
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* This function will take a URL string and convert it to a form useful for using in htaccess rules.
|
||
* Example: If URL passed to function = "http://www.mysite.com"
|
||
* Result = "http(s)?://(.*)?mysite\.com"
|
||
*
|
||
* @param string $url
|
||
* @return string
|
||
*/
|
||
public static function return_regularized_url($url) {
|
||
if (filter_var($url, FILTER_VALIDATE_URL)) {
|
||
$xyz = explode('.', $url);
|
||
$y = '';
|
||
if (count($xyz) > 1) {
|
||
$j = 1;
|
||
foreach ($xyz as $x) {
|
||
if (strpos($x, 'www') !== false) {
|
||
$y .= str_replace('www', '(.*)?', $x);
|
||
} elseif (1 == $j) {
|
||
$y .= $x;
|
||
} elseif ($j > 1) {
|
||
$y .= '\.' . $x;
|
||
}
|
||
$j++;
|
||
}
|
||
//Now replace the "http" with "http(s)?" to cover both secure and non-secure
|
||
if (strpos($y, 'https') !== false) {
|
||
$y = str_replace('https', 'http(s)?', $y);
|
||
} elseif (strpos($y, 'http') !== false) {
|
||
$y = str_replace('http', 'http(s)?', $y);
|
||
}
|
||
return $y;
|
||
} else {
|
||
return $url;
|
||
}
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns a string with <Files $filename> directive that contains rules
|
||
* to effectively block access to any file that has basename matching
|
||
* $filename under Apache webserver.
|
||
*
|
||
* @link http://httpd.apache.org/docs/current/mod/core.html#files
|
||
*
|
||
* @param string $filename
|
||
* @return string
|
||
*/
|
||
protected static function create_apache2_access_denied_rule($filename) {
|
||
return <<<END
|
||
<Files $filename>
|
||
<IfModule mod_authz_core.c>
|
||
Require all denied
|
||
</IfModule>
|
||
<IfModule !mod_authz_core.c>
|
||
Order deny,allow
|
||
Deny from all
|
||
</IfModule>
|
||
</Files>
|
||
|
||
END;
|
||
// Keep the empty line at the end of heredoc string,
|
||
// otherwise the string will not end with end-of-line character!
|
||
}
|
||
|
||
|
||
/**
|
||
* Convert an array of optionally asterisk-masked or partial IPv4 addresses
|
||
* into network/netmask notation. Netmask value for a "full" IP is not
|
||
* added (see example below)
|
||
*
|
||
* Example:
|
||
* In: array('1.2.3.4', '5.6', '7.8.9.*')
|
||
* Out: array('1.2.3.4', '5.6.0.0/16', '7.8.9.0/24')
|
||
*
|
||
* Simple validation is performed:
|
||
* In: array('1.2.3.4.5', 'abc', '1.2.xyz.4')
|
||
* Out: array()
|
||
*
|
||
* Simple sanitization is performed:
|
||
* In: array('6.7.*.9')
|
||
* Out: array('6.7.0.0/16')
|
||
*
|
||
* @param array $ips
|
||
* @return array
|
||
*/
|
||
protected static function add_netmask($ips = array()) {
|
||
|
||
$output = array();
|
||
if (empty($ips)) return array();
|
||
foreach ($ips as $ip) {
|
||
//Check if ipv6
|
||
if (strpos($ip, ':') !== false) {
|
||
//for now support whole ipv6 and CIDR range.
|
||
$checked_ip = AIOWPSecurity_Utility_IP::is_ipv6_address_or_ipv6_range($ip);
|
||
if (false != $checked_ip) {
|
||
$output[] = $ip;
|
||
}
|
||
}
|
||
|
||
$parts = explode('.', $ip);
|
||
|
||
// Skip any IP that is empty, has more parts than expected or has
|
||
// a non-numeric first part.
|
||
if (empty($parts) || (count($parts) > 4) || !is_numeric($parts[0])) {
|
||
continue;
|
||
}
|
||
|
||
$ip_out = array($parts[0]);
|
||
$netmask = 8;
|
||
|
||
for ($i = 1, $force_zero = false; ($i < 4) && $ip_out; $i++) {
|
||
if ($force_zero || !isset($parts[$i]) || ('' === $parts[$i]) || ('*' === $parts[$i])) {
|
||
$ip_out[$i] = '0';
|
||
$force_zero = true; // Forces all subsequent parts to be a zero
|
||
} elseif (is_numeric($parts[$i])) {
|
||
$ip_out[$i] = $parts[$i];
|
||
$netmask += 8;
|
||
} else {
|
||
// Invalid IP part detected, invalidate entire IP
|
||
$ip_out = false;
|
||
}
|
||
}
|
||
|
||
if ($ip_out) {
|
||
// Glue IP back together, add netmask if IP denotes a subnet, store for output.
|
||
$output[] = implode('.', $ip_out) . (($netmask < 32) ? ('/' . $netmask) : '');
|
||
}
|
||
}
|
||
|
||
return $output;
|
||
}
|
||
|
||
public static function get_htaccess_path() {
|
||
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
||
return $home_path . '.htaccess';
|
||
}
|
||
}
|