public static function menu_waf() { global $wp_filesystem; wp_enqueue_style('wordfence-jquery-ui-css', wfUtils::getBaseURL() . 'css/jquery-ui.min.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-jquery-ui-structure-css', wfUtils::getBaseURL() . 'css/jquery-ui.structure.min.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-jquery-ui-theme-css', wfUtils::getBaseURL() . 'css/jquery-ui.theme.min.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-jquery-ui-timepicker-css', wfUtils::getBaseURL() . 'css/jquery-ui-timepicker-addon.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-select2-css', wfUtils::getBaseURL() . 'css/select2.min.css', array(), WORDFENCE_VERSION); wp_enqueue_script('wordfence-timepicker-js', wfUtils::getBaseURL() . 'js/jquery-ui-timepicker-addon.js', array('jquery', 'jquery-ui-datepicker', 'jquery-ui-slider'), WORDFENCE_VERSION); wp_enqueue_script('wordfence-select2-js', wfUtils::getBaseURL() . 'js/select2.min.js', array('jquery'), WORDFENCE_VERSION); try { $wafData = self::_getWAFData(); } catch (wfWAFStorageFileConfigException $e) { // We don't have anywhere to write files in this scenario. Let's notify the user to update the permissions. $wafData = array(); $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH); if (function_exists('network_admin_url') && is_multisite()) { $wafMenuURL = network_admin_url('admin.php?page=WordfenceWAF&wafconfigrebuild=1'); } else { $wafMenuURL = admin_url('admin.php?page=WordfenceWAF&wafconfigrebuild=1'); } $wafMenuURL = add_query_arg(array('waf-nonce' => wp_create_nonce('wafconfigrebuild')), $wafMenuURL); $storageExceptionMessage = $e->getMessage() . ' <a href="' . esc_url($wafMenuURL) . '">Click here</a> to rebuild the configuration file.'; } catch (wfWAFStorageFileException $e) { // We don't have anywhere to write files in this scenario. Let's notify the user to update the permissions. $wafData = array(); $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH); $storageExceptionMessage = 'We were unable to write to ' . $logPath . ' which the WAF uses for storage. Please update permissions on the parent directory so the web server can write to it.'; } if (!empty($_GET['wafAction'])) { switch ($_GET['wafAction']) { case 'dismissAutoPrependNotice': check_admin_referer('wfDismissAutoPrependNotice', 'nonce'); wfConfig::set('dismissAutoPrependNotice', 1); break; case 'configureAutoPrepend': if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) { break; } $wfnonce = wp_create_nonce('wfWAFAutoPrepend'); $currentAutoPrependFile = ini_get('auto_prepend_file'); $currentAutoPrepend = !empty($_REQUEST['currentAutoPrepend']) ? $_REQUEST['currentAutoPrepend'] : null; $adminURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=' . $currentAutoPrepend); if ($currentAutoPrependFile && is_file($currentAutoPrependFile) && empty($currentAutoPrepend) && !WFWAF_SUBDIRECTORY_INSTALL) { $wafActionContent = sprintf("<p>The Wordfence Web Application Firewall is designed\nto run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially\nvulnerable code runs. This PHP setting is currently in use, and is including this file:</p>\n\n<pre class='wf-pre'>%s</pre>\n\n<p>If you don't recognize this file, please <a href='https://wordpress.org/support/plugin/wordfence'>contact us on the\nWordPress support forums</a> before proceeding.</p>\n\n<p>You can proceed with the installation and we will include this from within our <code>wordfence-waf.php</code> file\nwhich should maintain compatibility with your site, or you can opt to override the existing PHP setting.</p>\n\n<p>\n<a class='button button-primary' href='%s'>Include this file (Recommended)</a>\n<a class='button' href='%s'>Override this value</a>\n</p>\n", esc_html($currentAutoPrependFile), esc_url(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=include')), esc_url(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=override'))); break; } else { if (isset($_REQUEST['serverConfiguration'])) { check_admin_referer('wfWAFAutoPrepend', 'wfnonce'); $allow_relaxed_file_ownership = true; $helper = new wfWAFAutoPrependHelper($_REQUEST['serverConfiguration'], $currentAutoPrepend === 'override' ? null : $currentAutoPrependFile); if (($backups = $helper->getFilesNeededForBackup()) && empty($_REQUEST['confirmedBackup'])) { $wafActionContent = '<p>Please download a backup copy of the following files before we make the necessary changes:</p>'; $wafActionContent .= '<ul>'; foreach ($backups as $index => $backup) { $wafActionContent .= '<li><a class="button" onclick="wfWAFConfirmBackup(' . $index . ');" href="' . esc_url(add_query_arg(array('downloadBackup' => 1, 'backupIndex' => $index, 'serverConfiguration' => $helper->getServerConfig(), 'wfnonce' => $wfnonce), $adminURL)) . '">Download ' . esc_html(basename($backup)) . '</a></li>'; } $serverConfig = esc_attr($helper->getServerConfig()); $jsonBackups = json_encode(array_map('basename', $backups)); $adminURL = esc_url($adminURL); $wafActionContent .= "</ul>\n<form action='{$adminURL}' method='post'>\n<input type='hidden' name='wfnonce' value='{$wfnonce}'>\n<input type='hidden' value='{$serverConfig}' name='serverConfiguration'>\n<input type='hidden' value='1' name='confirmedBackup'>\n<button id='confirmed-backups' disabled class='button button-primary' type='submit'>Continue</button>\n</form>\n<script>\nvar wfWAFBackups = {$jsonBackups};\nvar wfWAFConfirmedBackups = [];\nfunction wfWAFConfirmBackup(index) {\n\twfWAFBackups[index] = false;\n\tvar confirmed = true;\n\tfor (var i = 0; i < wfWAFBackups.length; i++) {\n\t\tif (wfWAFBackups[i] !== false) {\n\t\t\tconfirmed = false;\n\t\t}\n\t}\n\tif (confirmed) {\n\t\tdocument.getElementById('confirmed-backups').disabled = false;\n\t}\n}\n</script>"; break; } ob_start(); if (false === ($credentials = request_filesystem_credentials($adminURL, '', false, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership))) { $wafActionContent = ob_get_clean(); break; } if (!WP_Filesystem($credentials, ABSPATH, $allow_relaxed_file_ownership)) { // Failed to connect, Error and request again request_filesystem_credentials($adminURL, '', true, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership); $wafActionContent = ob_get_clean(); break; } if ($wp_filesystem->errors->get_error_code()) { foreach ($wp_filesystem->errors->get_error_messages() as $message) { show_message($message); } $wafActionContent = ob_get_clean(); break; } ob_end_clean(); try { $helper->performInstallation($wp_filesystem); $adminURL = json_encode(esc_url_raw(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend&wafVerify=' . $wfnonce . '¤tAutoPrepend=' . $currentAutoPrepend))); $wafActionContent = "<script>\ndocument.location.href={$adminURL};\n</script>"; break; } catch (wfWAFAutoPrependHelperException $e) { $wafActionContent = "<p>" . $e->getMessage() . "</p>"; break; } } } $bootstrap = self::getWAFBootstrapPath(); // Auto populate drop down with server configuration // If no preconfiguration routine exists, output instructions for manual configuration $serverInfo = wfWebServerInfo::createFromEnvironment(); $dropdown = array(array("apache-mod_php", 'Apache + mod_php', $serverInfo->isApacheModPHP()), array("apache-suphp", 'Apache + suPHP', $serverInfo->isApacheSuPHP()), array("cgi", 'Apache + CGI/FastCGI', $serverInfo->isApache() && !$serverInfo->isApacheSuPHP() && ($serverInfo->isCGI() || $serverInfo->isFastCGI())), array("litespeed", 'LiteSpeed', $serverInfo->isLiteSpeed()), array("nginx", 'NGINX', $serverInfo->isNGINX()), array("iis", 'Windows (IIS)', $serverInfo->isIIS())); $wafActionContent = '<p>To be as secure as possible, the Wordfence Web Application Firewall is designed to run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially vulnerable code runs.</p> <div class="wf-notice"><strong>NOTE:</strong> If you have separate WordPress installations with Wordfence installed within a subdirectory of this site, it is recommended that you perform the Firewall installation procedure on those sites before this one.</div> '; $hasRecommendedOption = false; $wafPrependOptions = ''; foreach ($dropdown as $option) { list($optionValue, $optionText, $selected) = $option; $wafPrependOptions .= "<option value=\"{$optionValue}\"" . ($selected ? ' selected' : '') . ">{$optionText}" . ($selected ? ' (recommended based on our tests)' : '') . "</option>\n"; if ($selected) { $hasRecommendedOption = true; } } if (!$hasRecommendedOption) { $wafActionContent .= "<p>If you know your web server's configuration, please select it from the\nlist below:</p>"; } else { $wafActionContent .= "<p>We've preselected your server configuration based on our tests, but if\nyou know your web server's configuration, please select it now.</p>"; } $userIni = ini_get('user_ini.filename'); $nginxIniWarning = ''; if ($userIni) { $nginxIniWarning = "<div class='wf-notice wf-nginx-waf-config'>\nPart of the Firewall configuration procedure for NGINX depends on creating a <code>" . esc_html($userIni) . "</code> file\nin the root of your WordPress installation. This file can contain sensitive information and public access to it should\nbe restricted. We have\n<a href='https://docs.wordfence.com/en/Web_Application_Firewall_FAQ#NGINX'>instructions on our documentation site</a> on what\ndirectives to put in your nginx.conf to fix this.\n"; } $adminURL = esc_url($adminURL); $wafActionContent .= "\n<form action='{$adminURL}' method='post'>\n<input type='hidden' name='wfnonce' value='{$wfnonce}'>\n<select name='serverConfiguration' id='wf-waf-server-config'>\n{$wafPrependOptions}\n</select>\n<button class='button button-primary' type='submit'>Continue</button>\n</form>\n{$nginxIniWarning}\n</div>\n<script>\n(function(\$) {\n\tvar nginxNotice = \$('.wf-nginx-waf-config').hide();\n\t\$('#wf-waf-server-config').on('change', function() {\n\t\tvar el = \$(this);\n\t\tif (el.val() == 'nginx') {\n\t\t\tnginxNotice.fadeIn();\n\t\t} else {\n\t\t\tnginxNotice.fadeOut();\n\t\t}\n\t}).triggerHandler('change');\n})(jQuery);\n</script>\n"; $wafActionContent .= "\n<h3>Alternate method:</h3>\n<p>We've also included instructions to manually perform the change if you are using a web server other than what is listed in the drop-down, or if file permissions prevent this change.</p>"; $additionally = 'You'; if (!self::checkAndCreateBootstrap()) { $wafActionContent .= "<p>You will need create the following file in your WordPress root:</p>\n<pre class='wf-pre'>" . esc_html(self::getWAFBootstrapPath()) . "</pre>\n<p>You can create the file and set the permissions to allow WordPress to write to it, or you can add the code yourself:</p>\n<pre class='wf-pre'>" . esc_textarea(self::getWAFBootstrapContent()) . "</pre>"; $additionally = 'Additionally, you'; } $wafActionContent .= "<p>{$additionally} will need to append the following code to your <code>php.ini</code>:</p>\n<pre class='wf-pre'>auto_prepend_file = '" . esc_textarea($bootstrap) . "'</pre>"; $wafActionContent = sprintf('<div style="margin: 20px 0;">%s</div>', $wafActionContent); break; case '': break; } } require 'menu_waf.php'; }
public static function menu_waf() { global $wp_filesystem; wp_enqueue_style('wordfence-jquery-ui-css', wfUtils::getBaseURL() . 'css/jquery-ui.min.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-jquery-ui-structure-css', wfUtils::getBaseURL() . 'css/jquery-ui.structure.min.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-jquery-ui-theme-css', wfUtils::getBaseURL() . 'css/jquery-ui.theme.min.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-jquery-ui-timepicker-css', wfUtils::getBaseURL() . 'css/jquery-ui-timepicker-addon.css', array(), WORDFENCE_VERSION); wp_enqueue_style('wordfence-select2-css', wfUtils::getBaseURL() . 'css/select2.min.css', array(), WORDFENCE_VERSION); wp_enqueue_script('wordfence-timepicker-js', wfUtils::getBaseURL() . 'js/jquery-ui-timepicker-addon.js', array('jquery', 'jquery-ui-datepicker', 'jquery-ui-slider'), WORDFENCE_VERSION); wp_enqueue_script('wordfence-select2-js', wfUtils::getBaseURL() . 'js/select2.min.js', array('jquery'), WORDFENCE_VERSION); try { $wafData = self::_getWAFData(); } catch (wfWAFStorageFileConfigException $e) { // We don't have anywhere to write files in this scenario. Let's notify the user to update the permissions. $wafData = array(); $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH); if (function_exists('network_admin_url') && is_multisite()) { $wafMenuURL = network_admin_url('admin.php?page=WordfenceWAF&wafconfigrebuild=1'); } else { $wafMenuURL = admin_url('admin.php?page=WordfenceWAF&wafconfigrebuild=1'); } $wafMenuURL = add_query_arg(array('waf-nonce' => wp_create_nonce('wafconfigrebuild')), $wafMenuURL); $storageExceptionMessage = $e->getMessage() . ' <a href="' . esc_url($wafMenuURL) . '">Click here</a> to rebuild the configuration file.'; } catch (wfWAFStorageFileException $e) { // We don't have anywhere to write files in this scenario. Let's notify the user to update the permissions. $wafData = array(); $logPath = str_replace(ABSPATH, '~/', WFWAF_LOG_PATH); $storageExceptionMessage = 'We were unable to write to ' . $logPath . ' which the WAF uses for storage. Please update permissions on the parent directory so the web server can write to it.'; } if (!empty($_GET['wafAction'])) { switch ($_GET['wafAction']) { case 'dismissAutoPrependNotice': check_admin_referer('wfDismissAutoPrependNotice', 'nonce'); wfConfig::set('dismissAutoPrependNotice', 1); break; case 'updateSuPHPConfig': if (!isset($_REQUEST['updateComplete']) || !$_REQUEST['updateComplete']) { $wfnonce = wp_create_nonce('wfWAFUpdateSuPHPConfig'); $adminURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=updateSuPHPConfig'); $helper = new wfWAFAutoPrependHelper('apache-suphp'); if (!isset($_REQUEST['confirmedBackup'])) { if (($backups = $helper->getFilesNeededForBackup()) && empty($_REQUEST['confirmedBackup'])) { $wafActionContent = ' <h3>Wordfence Web Application Firewall Update</h3> <p>The Wordfence Web Application Firewall for Apache suPHP servers has been improved, and your configuration needs to be updated to continue receiving the best protection. Please download a backup copy of the following files before we make the necessary changes:</p>'; $wafActionContent .= '<ul>'; foreach ($backups as $index => $backup) { $wafActionContent .= '<li><a class="button" onclick="wfWAFConfirmBackup(' . $index . ');" href="' . esc_url(add_query_arg(array('downloadBackup' => 1, 'updateReady' => 1, 'backupIndex' => $index, 'wfnonce' => $wfnonce), $adminURL)) . '">Download ' . esc_html(basename($backup)) . '</a></li>'; } $jsonBackups = json_encode(array_map('basename', $backups)); $adminURL = esc_url($adminURL); $wafActionContent .= "</ul>\n\t<form action='{$adminURL}' method='post'>\n\t<input type='hidden' name='wfnonce' value='{$wfnonce}'>\n\t<input type='hidden' value='1' name='confirmedBackup'>\n\t<button id='confirmed-backups' disabled class='button button-primary' type='submit'>Continue</button>\n\t</form>\n\t<script>\n\tvar wfWAFBackups = {$jsonBackups};\n\tvar wfWAFConfirmedBackups = [];\n\tfunction wfWAFConfirmBackup(index) {\n\t\twfWAFBackups[index] = false;\n\t\tvar confirmed = true;\n\t\tfor (var i = 0; i < wfWAFBackups.length; i++) {\n\t\t\tif (wfWAFBackups[i] !== false) {\n\t\t\t\tconfirmed = false;\n\t\t\t}\n\t\t}\n\t\tif (confirmed) {\n\t\t\tdocument.getElementById('confirmed-backups').disabled = false;\n\t\t}\n\t}\n\t</script>"; $htaccessPath = $helper->getHtaccessPath(); $htaccess = @file_get_contents($htaccessPath); if ($htaccess && preg_match('/(# Wordfence WAF.*?# END Wordfence WAF)/is', $htaccess, $matches)) { $wafSection = $matches[1]; $wafSection = preg_replace('#(<IfModule\\s+mod_suphp\\.c>\\s+suPHP_ConfigPath\\s+\\S+\\s+</IfModule>)#i', '', $wafSection); $wafActionContent .= "<br>\n\t<h3>Alternate method:</h3>\n\t<p>We've also included instructions to manually perform the change if you are unable to do it automatically, or if file permissions prevent this change.</p>\n\t<p>You will need to replace the equivalent section from your .htaccess with the following:</p>\n\t<pre class='wf-pre'>" . htmlentities($wafSection) . "</pre>"; } break; } } else { check_admin_referer('wfWAFUpdateSuPHPConfig', 'wfnonce'); $allow_relaxed_file_ownership = true; ob_start(); if (false === ($credentials = request_filesystem_credentials($adminURL, '', false, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership))) { $wafActionContent = ob_get_clean(); break; } if (!WP_Filesystem($credentials, ABSPATH, $allow_relaxed_file_ownership)) { // Failed to connect, Error and request again request_filesystem_credentials($adminURL, '', true, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership); $wafActionContent = ob_get_clean(); break; } if ($wp_filesystem->errors->get_error_code()) { foreach ($wp_filesystem->errors->get_error_messages() as $message) { show_message($message); } $wafActionContent = ob_get_clean(); break; } ob_end_clean(); $htaccessPath = $helper->getHtaccessPath(); $htaccess = @file_get_contents($htaccessPath); if ($htaccess && preg_match('/(# Wordfence WAF.*?# END Wordfence WAF)/is', $htaccess, $matches)) { $wafSection = $matches[1]; $wafSection = preg_replace('#(<IfModule\\s+mod_suphp\\.c>\\s+suPHP_ConfigPath\\s+\\S+\\s+</IfModule>)#i', '', $wafSection); $htaccess = preg_replace('/# Wordfence WAF.*?# END Wordfence WAF/is', $wafSection, $htaccess); if (!$wp_filesystem->put_contents($htaccessPath, $htaccess)) { $wafActionContent = '<p>We were unable to make changes to the .htaccess file. It\'s possible WordPress cannot write to the .htaccess file because of file permissions, which may have been set by another security plugin, or you may have set them manually. Please verify the permissions allow the web server to write to the file, and retry the update.</p>'; break; } $adminURL = json_encode(esc_url_raw(network_admin_url('admin.php?page=WordfenceWAF&wafAction=updateSuPHPConfig&updateComplete=' . wp_create_nonce('wfWAFUpdateComplete')))); $wafActionContent = "<script>\n\tdocument.location.href={$adminURL};\n\t</script>"; break; } $wafActionContent = '<p>We were unable to make changes to the .htaccess file. It\'s possible WordPress cannot write to the .htaccess file because of file permissions, which may have been set by another security plugin, or you may have set them manually. Please verify the permissions allow the web server to write to the file, and retry the update.</p>'; } } break; case 'configureAutoPrepend': if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) { break; } $wfnonce = wp_create_nonce('wfWAFAutoPrepend'); $currentAutoPrependFile = ini_get('auto_prepend_file'); $currentAutoPrepend = !empty($_REQUEST['currentAutoPrepend']) ? $_REQUEST['currentAutoPrepend'] : null; $adminURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=' . $currentAutoPrepend); if ($currentAutoPrependFile && is_file($currentAutoPrependFile) && empty($currentAutoPrepend) && !WFWAF_SUBDIRECTORY_INSTALL) { $wafActionContent = sprintf("<p>The Wordfence Web Application Firewall is designed\nto run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially\nvulnerable code runs. This PHP setting is currently in use, and is including this file:</p>\n\n<pre class='wf-pre'>%s</pre>\n\n<p>If you don't recognize this file, please <a href='https://wordpress.org/support/plugin/wordfence'>contact us on the\nWordPress support forums</a> before proceeding.</p>\n\n<p>You can proceed with the installation and we will include this from within our <code>wordfence-waf.php</code> file\nwhich should maintain compatibility with your site, or you can opt to override the existing PHP setting.</p>\n\n<p>\n<a class='button button-primary' href='%s'>Include this file (Recommended)</a>\n<a class='button' href='%s'>Override this value</a>\n</p>\n", esc_html($currentAutoPrependFile), esc_url(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=include')), esc_url(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend¤tAutoPrepend=override'))); break; } else { if (isset($_REQUEST['serverConfiguration'])) { check_admin_referer('wfWAFAutoPrepend', 'wfnonce'); $allow_relaxed_file_ownership = true; $helper = new wfWAFAutoPrependHelper($_REQUEST['serverConfiguration'], $currentAutoPrepend === 'override' ? null : $currentAutoPrependFile); if (($backups = $helper->getFilesNeededForBackup()) && empty($_REQUEST['confirmedBackup'])) { $wafActionContent = '<p>Please download a backup copy of the following files before we make the necessary changes:</p>'; $wafActionContent .= '<ul>'; foreach ($backups as $index => $backup) { $wafActionContent .= '<li><a class="button" onclick="wfWAFConfirmBackup(' . $index . ');" href="' . esc_url(add_query_arg(array('downloadBackup' => 1, 'backupIndex' => $index, 'serverConfiguration' => $helper->getServerConfig(), 'wfnonce' => $wfnonce), $adminURL)) . '">Download ' . esc_html(basename($backup)) . '</a></li>'; } $serverConfig = esc_attr($helper->getServerConfig()); $jsonBackups = json_encode(array_map('basename', $backups)); $adminURL = esc_url($adminURL); $wafActionContent .= "</ul>\n<form action='{$adminURL}' method='post'>\n<input type='hidden' name='wfnonce' value='{$wfnonce}'>\n<input type='hidden' value='{$serverConfig}' name='serverConfiguration'>\n<input type='hidden' value='1' name='confirmedBackup'>\n<button id='confirmed-backups' disabled class='button button-primary' type='submit'>Continue</button>\n</form>\n<script>\nvar wfWAFBackups = {$jsonBackups};\nvar wfWAFConfirmedBackups = [];\nfunction wfWAFConfirmBackup(index) {\n\twfWAFBackups[index] = false;\n\tvar confirmed = true;\n\tfor (var i = 0; i < wfWAFBackups.length; i++) {\n\t\tif (wfWAFBackups[i] !== false) {\n\t\t\tconfirmed = false;\n\t\t}\n\t}\n\tif (confirmed) {\n\t\tdocument.getElementById('confirmed-backups').disabled = false;\n\t}\n}\n</script>"; break; } ob_start(); if (false === ($credentials = request_filesystem_credentials($adminURL, '', false, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership))) { $wafActionContent = ob_get_clean(); break; } if (!WP_Filesystem($credentials, ABSPATH, $allow_relaxed_file_ownership)) { // Failed to connect, Error and request again request_filesystem_credentials($adminURL, '', true, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership); $wafActionContent = ob_get_clean(); break; } if ($wp_filesystem->errors->get_error_code()) { foreach ($wp_filesystem->errors->get_error_messages() as $message) { show_message($message); } $wafActionContent = ob_get_clean(); break; } ob_end_clean(); try { $helper->performInstallation($wp_filesystem); $adminURL = json_encode(esc_url_raw(network_admin_url('admin.php?page=WordfenceWAF&wafAction=configureAutoPrepend&wafVerify=' . $wfnonce . '¤tAutoPrepend=' . $currentAutoPrepend))); $wafActionContent = "<script>\ndocument.location.href={$adminURL};\n</script>"; break; } catch (wfWAFAutoPrependHelperException $e) { $wafActionContent = "<p>" . $e->getMessage() . "</p>"; break; } } } $bootstrap = self::getWAFBootstrapPath(); // Auto populate drop down with server configuration // If no preconfiguration routine exists, output instructions for manual configuration $serverInfo = wfWebServerInfo::createFromEnvironment(); $dropdown = array(array("apache-mod_php", 'Apache + mod_php', $serverInfo->isApacheModPHP()), array("apache-suphp", 'Apache + suPHP', $serverInfo->isApacheSuPHP()), array("cgi", 'Apache + CGI/FastCGI', $serverInfo->isApache() && !$serverInfo->isApacheSuPHP() && ($serverInfo->isCGI() || $serverInfo->isFastCGI())), array("litespeed", 'LiteSpeed', $serverInfo->isLiteSpeed()), array("nginx", 'NGINX', $serverInfo->isNGINX()), array("iis", 'Windows (IIS)', $serverInfo->isIIS())); $wafActionContent = '<p>To be as secure as possible, the Wordfence Web Application Firewall is designed to run via a PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially vulnerable code runs.</p> <div class="wf-notice"><strong>NOTE:</strong> If you have separate WordPress installations with Wordfence installed within a subdirectory of this site, it is recommended that you perform the Firewall installation procedure on those sites before this one.</div> '; $hasRecommendedOption = false; $wafPrependOptions = ''; foreach ($dropdown as $option) { list($optionValue, $optionText, $selected) = $option; $wafPrependOptions .= "<option value=\"{$optionValue}\"" . ($selected ? ' selected' : '') . ">{$optionText}" . ($selected ? ' (recommended based on our tests)' : '') . "</option>\n"; if ($selected) { $hasRecommendedOption = true; } } if (!$hasRecommendedOption) { $wafActionContent .= "<p>If you know your web server's configuration, please select it from the\nlist below:</p>"; } else { $wafActionContent .= "<p>We've preselected your server configuration based on our tests, but if\nyou know your web server's configuration, please select it now.</p>"; } $userIni = ini_get('user_ini.filename'); $nginxIniWarning = ''; if ($userIni) { $nginxIniWarning = "<div class='wf-notice wf-nginx-waf-config'>\nPart of the Firewall configuration procedure for NGINX depends on creating a <code>" . esc_html($userIni) . "</code> file\nin the root of your WordPress installation. This file can contain sensitive information and public access to it should\nbe restricted. We have\n<a href='https://docs.wordfence.com/en/Web_Application_Firewall_FAQ#NGINX'>instructions on our documentation site</a> on what\ndirectives to put in your nginx.conf to fix this.\n"; } $adminURL = esc_url($adminURL); $wafActionContent .= "\n<form action='{$adminURL}' method='post'>\n<input type='hidden' name='wfnonce' value='{$wfnonce}'>\n<select name='serverConfiguration' id='wf-waf-server-config'>\n{$wafPrependOptions}\n</select>\n<button class='button button-primary' type='submit'>Continue</button>\n</form>\n{$nginxIniWarning}\n</div>\n<script>\n(function(\$) {\n\tvar nginxNotice = \$('.wf-nginx-waf-config').hide();\n\t\$('#wf-waf-server-config').on('change', function() {\n\t\tvar el = \$(this);\n\t\tif (el.val() == 'nginx') {\n\t\t\tnginxNotice.fadeIn();\n\t\t} else {\n\t\t\tnginxNotice.fadeOut();\n\t\t}\n\t}).triggerHandler('change');\n})(jQuery);\n</script>\n"; $wafActionContent .= "\n<h3>Alternate method:</h3>\n<p>We've also included instructions to manually perform the change if you are using a web server other than what is listed in the drop-down, or if file permissions prevent this change.</p>"; $additionally = 'You'; if (!self::checkAndCreateBootstrap()) { $wafActionContent .= "<p>You will need create the following file in your WordPress root:</p>\n<pre class='wf-pre'>" . esc_html(self::getWAFBootstrapPath()) . "</pre>\n<p>You can create the file and set the permissions to allow WordPress to write to it, or you can add the code yourself:</p>\n<pre class='wf-pre'>" . esc_textarea(self::getWAFBootstrapContent()) . "</pre>"; $additionally = 'Additionally, you'; } $wafActionContent .= "<p>{$additionally} will need to append the following code to your <code>php.ini</code>:</p>\n<pre class='wf-pre'>auto_prepend_file = '" . esc_textarea($bootstrap) . "'</pre>"; $wafActionContent = sprintf('<div style="margin: 20px 0;">%s</div>', $wafActionContent); break; case 'removeAutoPrepend': $installedHere = !(!WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL); $wfnonce = wp_create_nonce('wfWAFRemoveAutoPrepend'); $currentAutoPrependFile = ini_get('auto_prepend_file'); $adminURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=removeAutoPrepend'); if (!$currentAutoPrependFile && $installedHere) { //This should never happen but covering the possibility anyway $wafActionContent = "<p>Extended Protection Mode of the Wordfence Web Application Firewall uses the PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially\nvulnerable code runs. This PHP setting is not currently configured.</p>"; break; } else { if ($currentAutoPrependFile && is_file($currentAutoPrependFile) && !isset($_REQUEST['serverConfiguration']) && !isset($_REQUEST['iniModified']) && $installedHere && !WFWAF_SUBDIRECTORY_INSTALL) { $contents = file_get_contents($currentAutoPrependFile); $refersToWAF = preg_match('/define\\s*\\(\\s*(["\'])WFWAF_LOG_PATH\\1\\s*,\\s*(["\']).+?\\2\\s*\\)\\s*/', $contents); if ($refersToWAF) { $wafActionContent = sprintf("<p>Extended Protection Mode of the Wordfence Web Application Firewall uses the PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially\nvulnerable code runs. This PHP setting currently refers to the Wordfence file at:</p>\n\n<pre class='wf-pre'>%s</pre>\n\n<p>Before this file can be deleted, the configuration for the <code>auto_prepend_file</code> setting needs to be removed.</p>\n", esc_html($currentAutoPrependFile)); // Auto populate drop down with server configuration // If no preconfiguration routine exists, output instructions for manual configuration $serverInfo = wfWebServerInfo::createFromEnvironment(); $dropdown = array(array("apache-mod_php", 'Apache + mod_php', $serverInfo->isApacheModPHP()), array("apache-suphp", 'Apache + suPHP', $serverInfo->isApacheSuPHP()), array("cgi", 'Apache + CGI/FastCGI', $serverInfo->isApache() && !$serverInfo->isApacheSuPHP() && ($serverInfo->isCGI() || $serverInfo->isFastCGI())), array("litespeed", 'LiteSpeed', $serverInfo->isLiteSpeed()), array("nginx", 'NGINX', $serverInfo->isNGINX()), array("iis", 'Windows (IIS)', $serverInfo->isIIS())); $hasRecommendedOption = false; $wafPrependOptions = ''; foreach ($dropdown as $option) { list($optionValue, $optionText, $selected) = $option; $wafPrependOptions .= "<option value=\"{$optionValue}\"" . ($selected ? ' selected' : '') . ">{$optionText}" . ($selected ? ' (recommended based on our tests)' : '') . "</option>\n"; if ($selected) { $hasRecommendedOption = true; } } if (!$hasRecommendedOption) { $wafActionContent .= "<p>If you know your web server's configuration, please select it from the\nlist below:</p>"; } else { $wafActionContent .= "<p>We've preselected your server configuration based on our tests, but if\nyou know your web server's configuration, please select it now.</p>"; } $adminURL = esc_url($adminURL); $wafActionContent .= "\n<form action='{$adminURL}' method='post'>\n<input type='hidden' name='wfnonce' value='{$wfnonce}'>\n<select name='serverConfiguration' id='wf-waf-server-config'>\n{$wafPrependOptions}\n</select>\n<button class='button button-primary' type='submit'>Continue</button>\n</form>\n</div>\n"; } else { //This point should only be reached if the wordfence-waf.php file format changes enough for the detection to fail and this isn't updated to match $wafActionContent = sprintf("<p>Extended Protection Mode of the Wordfence Web Application Firewall uses the PHP ini setting called <code>auto_prepend_file</code> in order to ensure it runs before any potentially\nvulnerable code runs. This PHP setting currently refers to an unknown file at:</p>\n\n<pre class='wf-pre'>%s</pre>\n\n<p>Automatic uninstallation cannot be completed, but you may still be able to <a href='%s' target='_blank'>manually uninstall extended protection</a>.</p>\n", esc_html($currentAutoPrependFile), esc_url('https://docs.wordfence.com/en/Web_Application_Firewall_FAQ#How_can_I_remove_the_firewall_setup_manually.3F')); break; } } else { if (isset($_REQUEST['serverConfiguration'])) { check_admin_referer('wfWAFRemoveAutoPrepend', 'wfnonce'); $allow_relaxed_file_ownership = true; $helper = new wfWAFAutoPrependHelper($_REQUEST['serverConfiguration'], null); $serverConfig = esc_attr($helper->getServerConfig()); if ($installedHere && empty($_REQUEST['iniModified']) && ($backups = $helper->getFilesNeededForBackup()) && empty($_REQUEST['confirmedBackup'])) { $wafActionContent = '<p>Please download a backup copy of the following files before we make the necessary changes:</p>'; $wafActionContent .= '<ul>'; foreach ($backups as $index => $backup) { $wafActionContent .= '<li><a class="button" onclick="wfWAFConfirmBackup(' . $index . ');" href="' . esc_url(add_query_arg(array('downloadBackup' => 1, 'backupIndex' => $index, 'serverConfiguration' => $helper->getServerConfig(), 'wfnonce' => $wfnonce), $adminURL)) . '">Download ' . esc_html(basename($backup)) . '</a></li>'; } $jsonBackups = json_encode(array_map('basename', $backups)); $adminURL = esc_url($adminURL); $wafActionContent .= "</ul>\n<form action='{$adminURL}' method='post'>\n<input type='hidden' name='wfnonce' value='{$wfnonce}'>\n<input type='hidden' value='{$serverConfig}' name='serverConfiguration'>\n<input type='hidden' value='1' name='confirmedBackup'>\n<button id='confirmed-backups' disabled class='button button-primary' type='submit'>Continue</button>\n</form>\n<script>\nvar wfWAFBackups = {$jsonBackups};\nvar wfWAFConfirmedBackups = [];\nfunction wfWAFConfirmBackup(index) {\n\twfWAFBackups[index] = false;\n\tvar confirmed = true;\n\tfor (var i = 0; i < wfWAFBackups.length; i++) {\n\t\tif (wfWAFBackups[i] !== false) {\n\t\t\tconfirmed = false;\n\t\t}\n\t}\n\tif (confirmed) {\n\t\tdocument.getElementById('confirmed-backups').disabled = false;\n\t}\n}\n</script>"; break; } ob_start(); if (false === ($credentials = request_filesystem_credentials($adminURL, '', false, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership))) { $wafActionContent = ob_get_clean(); break; } if (!WP_Filesystem($credentials, ABSPATH, $allow_relaxed_file_ownership)) { // Failed to connect, Error and request again request_filesystem_credentials($adminURL, '', true, ABSPATH, array('version', 'locale'), $allow_relaxed_file_ownership); $wafActionContent = ob_get_clean(); break; } if ($wp_filesystem->errors->get_error_code()) { foreach ($wp_filesystem->errors->get_error_messages() as $message) { show_message($message); } $wafActionContent = ob_get_clean(); break; } ob_end_clean(); try { if (isset($_REQUEST['iniModified'])) { $usesUserIni = $helper->usesUserIni(); if ((!WFWAF_AUTO_PREPEND || WFWAF_SUBDIRECTORY_INSTALL) && (!$usesUserIni || $usesUserIni && isset($_REQUEST['iniTTLWaited']))) { //WFWAF_AUTO_PREPEND can be false for a brief time when using .user.ini, so make sure we've waited the TTL before removing the wordfence-waf.php file $helper->performAutoPrependFileRemoval($wp_filesystem); $adminURL = json_encode(esc_url_raw(network_admin_url('admin.php?page=WordfenceWAF&wafRemoved=' . $wfnonce))); $wafActionContent = "<p>Removing firewall files...</p><script>\ndocument.location.href={$adminURL};\n</script>"; } else { //Using a .user.ini where there's a delay before taking effect $iniTTL = intval(ini_get('user_ini.cache_ttl')); if ($iniTTL == 0) { $iniTTL = 300; //The PHP default } $timeout = max(30000, ($iniTTL + 1) * 1000); if ($timeout < 60000) { $timeoutString = floor($timeout / 1000) . ' second' . ($timeout == 1000 ? '' : 's'); } else { $timeoutString = floor($timeout / 60000) . ' minute' . (floor($timeout / 60000) == 1 ? '' : 's'); } $wafActionContent = "<h3>Finishing Removal</h3>"; if (isset($_REQUEST['iniTTLWaited'])) { $wafActionContent .= "<p class='wf-error'>Extended Protection Mode has not been disabled. This may be because <code>auto_prepend_file</code> is configured somewhere else or the value is still cached by PHP.</p>"; } else { $wafActionContent .= "<p>The <code>auto_prepend_file</code> setting has been successfully removed from <code>.htaccess</code> and <code>.user.ini</code>. Once this change takes effect, Extended Protection Mode will be disabled.</p>\n"; if ($_REQUEST['manualAutoPrependReenable']) { $wafActionContent .= "<p>Any previous value for <code>auto_prepend_file</code> will need to be re-enabled manually if needed.</p>"; } $wafActionContent .= "<p class='wordfence-waiting'><img src='" . wfUtils::getBaseURL() . "images/loading_large.gif' alt='Loading indicator'> <span>Waiting for it to take effect. This may take up to {$timeoutString}.</span></p>"; } $adminURL = json_encode(esc_url_raw(network_admin_url('admin.php?page=WordfenceWAF&wafAction=removeAutoPrepend&wfnonce=' . $wfnonce . '&serverConfiguration=' . $serverConfig . '&iniModified=1&iniTTLWaited=1'))); $wafActionContent .= "<script>\nsetTimeout(function() { document.location.href={$adminURL}; }, {$timeout});\n</script>"; } } else { if ($installedHere) { $hasCommentedAutoPrepend = $helper->performIniRemoval($wp_filesystem); $adminURL = json_encode(esc_url_raw(network_admin_url('admin.php?page=WordfenceWAF&wafAction=removeAutoPrepend&wfnonce=' . $wfnonce . '&serverConfiguration=' . $serverConfig . '&iniModified=1&manualAutoPrependReenable=' . ($hasCommentedAutoPrepend ? 1 : 0)))); $wafActionContent = "<script>\ndocument.location.href={$adminURL};\n</script>"; } } break; } catch (wfWAFAutoPrependHelperException $e) { $wafActionContent = "<p>" . $e->getMessage() . "</p>"; break; } } } } $bootstrap = self::getWAFBootstrapPath(); $wafActionContent .= "<br>\n<h3>Alternate method:</h3>\n<p>We've also included instructions to manually perform the change if you are using a web server other than what is listed in the drop-down, or if file permissions prevent this change.</p>"; $wafActionContent .= "<p>You will need to remove the following code from your <code>php.ini</code>, <code>.user.ini</code>, or <code>.htaccess</code> file:</p>\n<pre class='wf-pre'>auto_prepend_file = '" . esc_textarea($currentAutoPrependFile) . "'</pre>\n<p>Once the change takes effect, you will need remove the following file in your WordPress root:</p>\n<pre class='wf-pre'>" . esc_html(self::getWAFBootstrapPath()) . "</pre>"; $wafActionContent = sprintf('<div style="margin: 20px 0;">%s</div>', $wafActionContent); break; case '': break; } } require 'menu_waf.php'; }