From d8e1eb150afd037fcba5c7dbb6c9611fed4dfb36 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 21:21:36 +0100 Subject: [PATCH 01/14] fix: Fixed incorrect rule name --- src/SubCommands/BlockPhpExecutionInWpIncludes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SubCommands/BlockPhpExecutionInWpIncludes.php b/src/SubCommands/BlockPhpExecutionInWpIncludes.php index 81c0fa2..8f3eb61 100644 --- a/src/SubCommands/BlockPhpExecutionInWpIncludes.php +++ b/src/SubCommands/BlockPhpExecutionInWpIncludes.php @@ -4,7 +4,7 @@ class BlockPhpExecutionInWpIncludes extends SubCommand { public string $ruleTemplate = 'block_php_execution_in_wp_includes'; - public string $ruleName = 'BLOCK PHP EXECUTION IN UPLOADS'; + public string $ruleName = 'BLOCK PHP EXECUTION IN WP INCLUDES'; public string $successMessage = 'Block Execution In wp-includes Directory rule has been deployed.'; public string $removalMessage= 'Block Execution In wp-includes Directory rule has been removed.'; } \ No newline at end of file From d6c703021123533df4109483506e36da941b2c92 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 21:30:18 +0100 Subject: [PATCH 02/14] feat: Refactoring of the block access command --- src/SecureCommand.php | 73 +++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/src/SecureCommand.php b/src/SecureCommand.php index 263f2ba..574d936 100644 --- a/src/SecureCommand.php +++ b/src/SecureCommand.php @@ -33,7 +33,7 @@ * Success: Directory Browsing is disabled. * * # Remove security rule - * $ wp secure disable-directory-browsing --disable + * $ wp secure disable-directory-browsing --remove * Success: Directory Browsing is enabled. * * # Remove all security rules @@ -76,11 +76,10 @@ class SecureCommand extends WP_CLI_Command { * * ## EXAMPLES * - * $ wp secure disable_directory_browsing + * $ wp secure disable-directory-browsing * Success: Directory Browsing security rule is now active. * * @subcommand disable-directory-browsing - * * @when before_wp_load */ public function disable_directory_browsing($args, $assoc_args) : void { @@ -88,15 +87,14 @@ public function disable_directory_browsing($args, $assoc_args) : void { } /** - * Disables execution of PHP files in Plugins. + * Disables execution of PHP files in Plugins, Uploads, Themes and wp-includes. * - * PHP files in `wp-content/plugins` directory shouldn't be directly accessible. This is important in case of malware injection as it prevents attacker - * from directly accessing infected PHP files + * PHP files in certain directories shouldn't be directly accessible. This is important in case of malware injection as it prevents attacker from directly accessing infected PHP files * * ## OPTIONS * - * - * : Required. accepts: plugins, uploads, includes, themes or all. + * + * : Required. Accepts: plugins, uploads, includes, themes or all. * * [--remove] * : Removes the rule from .htaccess or nginx.conf. @@ -113,20 +111,17 @@ public function disable_directory_browsing($args, $assoc_args) : void { * * ## EXAMPLES * - * # Apply the block rules for plugins + * # Apply the block rules for plugins directory * $ wp secure block-php plugins * Success: Block Execution In Plugins Directory rule has been deployed. * - * # Apply the block rules for all parts. + * # Apply the block rules for all directories * $ wp secure block-php all - * Success: Block Execution In Plugins Directory rule has been deployed. - * - * @when before_wp_load * * @subcommand block-php-execution + * @when before_wp_load */ public function block_php($args, $assoc_args) : void { - $block_part = $args[0]; // Failure first. @@ -163,7 +158,7 @@ public function block_php($args, $assoc_args) : void { * * ## OPTIONS * - * + * * : This option is required. Accepts one of the following values: sensitive-files, sensitive-directories, htaccess, xmlrpc or all. * * [--remove] @@ -192,34 +187,44 @@ public function block_php($args, $assoc_args) : void { * $ wp secure block-access all * Success: Block Access to Sensitive Files rule has been deployed. * - * @subcommand block-access + * # Block custom files and directories + * $ wp secure block-access custom --files=dump.sql --directories=some/directory + * Success: Block Access to Sensitive Files rule has been deployed. * + * @subcommand block-access * @when before_wp_load */ public function block_access($args, $assoc_args): void { - $block_part = $args[0]; + $blockPart = $args[0]; + + $allowedSubArguments = [ + 'sensitive-files', 'sensitive-directories', 'htaccess', 'xmlrpc', 'all', 'custom' + ]; // Failure first. - if ( ! in_array( $block_part, array( 'sensitive-files', 'sensitive-directories', 'htaccess', 'xmlrpc', 'all' ), true ) ) { - WP_CLI::error( sprintf( 'Invalid block part "%s" was provided. Allowed values are "files", "directories", "htaccess", "xmlrpc" or "all"', $block_part ) ); + if(!in_array( $blockPart, $allowedSubArguments, true)) { + WP_CLI::error(sprintf('Invalid block part "%s" was provided. Allowed values are ' . implode(', ', $allowedSubArguments), $blockPart)); } - if ( 'all' === $block_part || 'sensitive-files' === $block_part ) { - WP_CLI::debug( 'Blocking access to the sensitive files.', 'secure'); + if(in_array($blockPart, ['all', 'custom', 'sensitive-files'])) { + WP_CLI::debug('Blocking access to the sensitive files.', 'secure'); (new BlockAccessToSensitiveFiles($assoc_args))->output(); - } - if ( 'all' === $block_part || 'sensitive-directories' === $block_part ) { - WP_CLI::debug( 'Blocking access to the directories.', 'secure'); - ( new BlockAccessToSensitiveDirectories( $assoc_args ) )->output(); - } - if ( 'all' === $block_part || 'htaccess' === $block_part ) { - WP_CLI::debug( 'Blocking access to the htaccess.', 'secure'); - (new BlockAccessToHtaccess($assoc_args))->output(); - } - if ( 'all' === $block_part || 'xmlrpc' === $block_part ) { - WP_CLI::debug( 'Blocking access to the xmlrpc.', 'secure'); - (new BlockAccessToXmlRpc($assoc_args))->output(); - } + } + + if(in_array($blockPart, ['all', 'custom', 'sensitive-directories'])) { + WP_CLI::debug('Blocking access to the directories.', 'secure'); + (new BlockAccessToSensitiveDirectories($assoc_args))->output(); + } + + if(in_array($blockPart, ['all', 'htaccess'])) { + WP_CLI::debug('Blocking access to the htaccess.', 'secure'); + (new BlockAccessToHtaccess($assoc_args))->output(); + } + + if(in_array($blockPart, ['all', 'xmlrpc'])) { + WP_CLI::debug('Blocking access to the xmlrpc.', 'secure'); + (new BlockAccessToXmlRpc($assoc_args))->output(); + } } /** From 1fbeb810de9bc62315aca929e110ba630a324b78 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 21:38:45 +0100 Subject: [PATCH 03/14] feat: Refactoring of the block php execution command --- src/SecureCommand.php | 52 ++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/SecureCommand.php b/src/SecureCommand.php index 574d936..5976e5b 100644 --- a/src/SecureCommand.php +++ b/src/SecureCommand.php @@ -122,33 +122,39 @@ public function disable_directory_browsing($args, $assoc_args) : void { * @when before_wp_load */ public function block_php($args, $assoc_args) : void { - $block_part = $args[0]; + $blockPart = $args[0]; + + $allowedArguments = [ + 'plugins', 'uploads', 'wp-includes', 'themes', 'all' + ]; // Failure first. - if ( ! in_array( $block_part, - array( 'plugins', 'uploads', 'wp-includes', 'themes', 'all' ), - true ) - ) { - WP_CLI::error( sprintf( 'Invalid block part "%s" was provided. Allowed values are "plugins", "uploads", "includes", "themes" or "all"', - $block_part ) ); + if(!in_array($blockPart, $allowedArguments, true)) { + WP_CLI::error( + sprintf('Invalid block part "%s" was provided. Allowed values are "plugins", "uploads", "includes", "themes" or "all"', + $blockPart) + ); } - if ( 'all' === $block_part || 'plugins' === $block_part ) { - WP_CLI::debug( 'Securing the plugins folder.', 'secure'); - ( new BlockPhpExecutionInPlugins( $assoc_args ) )->output(); - } - if ( 'all' === $block_part || 'uploads' === $block_part ) { - WP_CLI::debug( 'Securing the uploads folder.', 'secure'); - ( new BlockPhpExecutionInUploads( $assoc_args ) )->output(); - } - if ( 'all' === $block_part || 'wp-includes' === $block_part ) { - WP_CLI::debug( 'Securing the includes folder.', 'secure'); - ( new BlockPhpExecutionInWpIncludes( $assoc_args ) )->output(); - } - if ( 'all' === $block_part || 'themes' === $block_part ) { - WP_CLI::debug( 'Securing the themes folder.', 'secure'); - ( new BlockPhpExecutionInThemes( $assoc_args ) )->output(); - } + if(in_array($blockPart, ['all', 'plugins'])) { + WP_CLI::debug('Securing the plugins folder.', 'secure'); + (new BlockPhpExecutionInPlugins($assoc_args))->output(); + } + + if(in_array($blockPart, ['all', 'uploads'])) { + WP_CLI::debug('Securing the uploads folder.', 'secure'); + (new BlockPhpExecutionInUploads($assoc_args))->output(); + } + + if(in_array($blockPart, ['all', 'wp-includes'])) { + WP_CLI::debug('Securing the wp-includes folder.', 'secure'); + (new BlockPhpExecutionInWpIncludes($assoc_args))->output(); + } + + if(in_array($blockPart, ['all', 'themes'])) { + WP_CLI::debug('Securing the themes folder.', 'secure'); + (new BlockPhpExecutionInThemes($assoc_args))->output(); + } } /** From 7b499b5e0fbfa961fc19dc4e970108e310a1619a Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 21:45:10 +0100 Subject: [PATCH 04/14] docs: Updated documentation for all commands --- src/SecureCommand.php | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/SecureCommand.php b/src/SecureCommand.php index 5976e5b..1a657ec 100644 --- a/src/SecureCommand.php +++ b/src/SecureCommand.php @@ -160,12 +160,15 @@ public function block_php($args, $assoc_args) : void { /** * Blocks direct access to various sensitive files and directories * - * Blocks direct access to readme.html, readme.txt, wp-config.php and wp-admin/install.php files. + * Blocks direct access to sensitive files such as readme.html, readme.txt, wp-config.php and wp-admin/install.php files. + * It also blocks the direct access to a certain number of directories such as .git, svn, cache and vendors. + * + * You can use this command to block access to custom files and folders as well. * * ## OPTIONS * * - * : This option is required. Accepts one of the following values: sensitive-files, sensitive-directories, htaccess, xmlrpc or all. + * : This option is required. Accepts one of the following values: sensitive-files, sensitive-directories, htaccess, xmlrpc, custom or all. * * [--remove] * : Removes the rule from .htaccess or nginx.conf. @@ -259,11 +262,10 @@ public function block_access($args, $assoc_args): void { * * ## EXAMPLES * - * $ wp secure block_author_scanning + * $ wp secure block-author-scanning * Success: Block Author Scanning rule has been deployed. * * @subcommand block-author-scanning - * * @when before_wp_load */ public function block_author_scanning($args, $assoc_args) : void { @@ -273,8 +275,8 @@ public function block_author_scanning($args, $assoc_args) : void { /** * Removes all WP CLI Secure rules * - * Use this command to remove all deployed security rules. If you are using nginx you need to restart it. If you copied rules manually, this command - * will not remove them! + * Use this command to remove all deployed security rules. If you are using nginx you need to restart it. + * If you copied rules manually, this command will not remove them! * * ## OPTIONS * @@ -316,18 +318,22 @@ public function flush($args, $assoc_args) : void { * @subcommand integrity-scan * @when before_wp_load */ - public function integrityscan($args, $assoc_args) : void { + public function integrity_scan($args, $assoc_args) : void { WP_CLI::runcommand('core verify-checksums'); } /** * Disable the file editor in WordPress * - * @subcommand disable-file-editor + * The problem with the WordPress file editor is that it allows users to run PHP code on your site. + * Anytime a user is able to run their own code, this presents a security risk. + * If an insecure admin account is hacked, the WordPress file editor is the gateway through which a full-fledged attack can be + * carried out. * * @param $args * @param $assoc_args * + * @subcommand disable-file-editor * @when before_wp_load * * @return void @@ -337,15 +343,17 @@ public function disable_file_editor($args, $assoc_args) : void { } /** - * Fix all directory and file permissions of the wordpress installation + * Fix all directory and file permissions of the WordPress installation + * + * Use this command to verify that the permissions of all files and directories are set according the WordPress recommendations. + * This command will set 0666 to all files and 0755 to all folders inside WordPress installation. * - * Use this command to verify that the permissions of all files and directories are set according the wordpress recommendations. - * IMPORTANT: Don't use this command if you don't know what you are doing here! + * IMPORTANT: Don't use this command if you don't know what you are doing here! * * ## EXAMPLES * * $ wp secure fix-permissions - * Success: All permission are reset to wordpress default. + * Success: All permission are set to the WordPress recommended values. * * @subcommand fix-permissions * @when before_wp_load @@ -359,6 +367,8 @@ public function fix_permissions($args, $assoc_args) : void { /** * Deploys all security rules at once * + * This command will deploy all security rules at once. + * * ## EXAMPLES * * $ wp secure all From b4f061b3279d256cda2fa4fcbfb06eaafc646992 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 21:48:25 +0100 Subject: [PATCH 05/14] fix: Fixed incorrect git directory name --- .../BlockAccessToSensitiveDirectories.php | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/SubCommands/BlockAccessToSensitiveDirectories.php b/src/SubCommands/BlockAccessToSensitiveDirectories.php index 03c5df2..dc1b2b2 100644 --- a/src/SubCommands/BlockAccessToSensitiveDirectories.php +++ b/src/SubCommands/BlockAccessToSensitiveDirectories.php @@ -8,17 +8,26 @@ class BlockAccessToSensitiveDirectories extends SubCommand { public string $successMessage = 'Block Access to Sensitive Directories rule has been deployed.'; public string $removalMessage= 'Block Access to Sensitive Directories rule has been removed.'; - public function getTemplateVars() { - $directories = isset( $this->commandArguments['directories'] ) ? $this->commandArguments['directories'] : 'git,svn,vendors,cache'; - if ( ! empty( $directories ) ) { - $directories = explode( ',', $directories ); - $directories = array_map( 'trim', $directories ); + /** + * @var string Default directories that we are going to protect + */ + private string $sensitiveDirectories = '.git,svn,vendors,cache'; + + /** + * @return array + */ + public function getTemplateVars() : array { + $directories = $this->commandArguments['directories'] ?? $this->sensitiveDirectories; + if (!empty($directories)) { + $directories = explode(',', $directories); + $directories = array_map('trim', $directories); $directories_array = []; return [ - [ 'directories' => implode( '|', array_map( 'preg_quote', $directories ) ) ] + ['directories' => implode('|', array_map('preg_quote', $directories))] ]; } + return []; } } \ No newline at end of file From 3c8458b2cfe7d034f4e8906ac931f27cc72b640c Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 21:57:57 +0100 Subject: [PATCH 06/14] feat: Refactoring of the Sensitive Files class --- .../BlockAccessToSensitiveFiles.php | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/SubCommands/BlockAccessToSensitiveFiles.php b/src/SubCommands/BlockAccessToSensitiveFiles.php index 9f6a2b1..6e78477 100644 --- a/src/SubCommands/BlockAccessToSensitiveFiles.php +++ b/src/SubCommands/BlockAccessToSensitiveFiles.php @@ -8,20 +8,32 @@ class BlockAccessToSensitiveFiles extends SubCommand { public string $successMessage = 'Block Access to Sensitive Files rule has been deployed.'; public string $removalMessage= 'Block Access to Sensitive Files rule has been removed.'; - public function getTemplateVars() { - $files = isset( $this->commandArguments['files'] ) ? $this->commandArguments['files'] : 'readme.html, readme.txt, wp-config.php, wp-admin/install.php'; - if ( ! empty( $files ) ) { - $files = explode( ',', $files ); - $files = array_map( 'trim', $files ); + /** + * @var string A list of files that should be protected by default + */ + private string $sensitiveFiles = 'readme.html, readme.txt, wp-config.php, wp-admin/install.php'; + + /** + * @return array + */ + public function getTemplateVars(): array { + $files = $this->commandArguments['files'] ?? $this->sensitiveFiles; + + if (!empty($files)) { + $files = explode(',', $files); + $files = array_map('trim', $files); $files_array = []; - foreach ( $files as $key => $value ) { - $file = isset( $this->commandArguments['server'] ) && $this->commandArguments['server'] === 'nginx' ? preg_quote( $value ) : $value; - $files_array[] = [ 'file' => $file ]; + foreach ($files as $key => $value) { + $file = (isset($this->commandArguments['server']) && $this->commandArguments['server'] === 'nginx') ? + preg_quote($value) : $value; + + $files_array[] = ['file' => $file]; } return $files_array; } + return []; } } \ No newline at end of file From 4f9dc48c339b1f1e2e27115b4a5fade3ab267c17 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:02:14 +0100 Subject: [PATCH 07/14] feat: Refactoring, code comment updates --- src/SubCommands/FixFileAndDirPermissions.php | 2 +- src/SubCommands/SubCommand.php | 33 ++++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/SubCommands/FixFileAndDirPermissions.php b/src/SubCommands/FixFileAndDirPermissions.php index 455c004..241fd9e 100644 --- a/src/SubCommands/FixFileAndDirPermissions.php +++ b/src/SubCommands/FixFileAndDirPermissions.php @@ -19,12 +19,12 @@ class FixFileAndDirPermissions { * @return bool */ public function fixPermissions() : bool { + //Stop execution if ABSPATH is not defined to prevent changing permissions in the wrong place if(!defined('ABSPATH')) { return false; } $iterator = new \RecursiveDirectoryIterator(ABSPATH); - foreach($iterator as $file) { chmod($file, is_file($file) ? $this->filePermissions : $this->directoryPermissions); } diff --git a/src/SubCommands/SubCommand.php b/src/SubCommands/SubCommand.php index 700f08f..ffd5757 100644 --- a/src/SubCommands/SubCommand.php +++ b/src/SubCommands/SubCommand.php @@ -120,10 +120,11 @@ private function setRuleContent() : array { } unset($file); + //Combine templates and command arguments, if any + //This is used for block-access command $result = new RuleContent( $result, $this->getTemplateVars() ); - $result = $result->getContent(); - return $result; + return $result->getContent(); } /** @@ -131,7 +132,7 @@ private function setRuleContent() : array { * * @return array */ - public function getTemplateVars() { + public function getTemplateVars(): array { return []; } @@ -161,30 +162,30 @@ private function getOutputMessage(string $type = 'success') : string { */ public function output() { try { - $fileManager = new FileManager( $this->filePath ); - if ( $this->output ) { - $content = $fileManager->wrap( $this->ruleContent, 'block', $this->ruleName ); + $fileManager = new FileManager($this->filePath); + if ($this->output) { + $content = $fileManager->wrap($this->ruleContent, 'block', $this->ruleName); WP_CLI::line( implode( PHP_EOL, $content ) ); } else { - if ( isset( $this->commandArguments['remove'] ) && $this->commandArguments['remove'] === true ) { + if (isset($this->commandArguments['remove']) && $this->commandArguments['remove'] === true) { //We need to remove the rule from file - $result = $fileManager->remove( $this->ruleName ); + $result = $fileManager->remove($this->ruleName); - if ( $result ) { - WP_CLI::success( $this->getOutputMessage( 'removal' ) ); + if ($result) { + WP_CLI::success($this->getOutputMessage('removal')); } } else { //Add the rule - $fileManager->add( $this->ruleContent, $this->ruleName ); + $fileManager->add($this->ruleContent, $this->ruleName); - WP_CLI::success( $this->getOutputMessage( 'success' ) ); + WP_CLI::success($this->getOutputMessage('success')); } } - } catch ( FileDoesNotExist | FileIsNotWritable | FileIsNotReadable $e ) { - WP_CLI::error( $e->getMessage() ); - } catch ( RuleAlreadyExist $e ) { - WP_CLI::warning( $e->getMessage() ); + } catch (FileDoesNotExist | FileIsNotWritable | FileIsNotReadable $e) { + WP_CLI::error($e->getMessage()); + } catch (RuleAlreadyExist $e) { + WP_CLI::warning($e->getMessage()); } } } From b49dc9a07766abf9bf6132c3131721fd88e73cf6 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:12:57 +0100 Subject: [PATCH 08/14] fix: Fixed incorrect path to the test assets --- tests/Unit/FileManager/WriteTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/FileManager/WriteTest.php b/tests/Unit/FileManager/WriteTest.php index ec6b18e..dc2bcf7 100644 --- a/tests/Unit/FileManager/WriteTest.php +++ b/tests/Unit/FileManager/WriteTest.php @@ -41,8 +41,8 @@ class WriteTest extends BaseTestCase { public function setUp(): void { parent::setUp(); - $content = file_get_contents(dirname(__DIR__) . '/assets/htaccess-base.txt'); - $content2 = file_get_contents(dirname(__DIR__) . '/assets/htaccess-secured.txt'); + $content = file_get_contents(getcwd() . '/tests/assets/htaccess-base.txt'); + $content2 = file_get_contents(getcwd() . '/tests/assets/htaccess-secured.txt'); $this->file = FileHelper::create('.htaccess', 0755, $content); $this->file2 = FileHelper::create('.htaccess2', 0777, $content); $this->file3 = FileHelper::create('.htaccess-secured', 0666, $content2); From 07fafa960201d2d3569f261b66440f92c451f7ca Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:25:59 +0100 Subject: [PATCH 09/14] fix: Fixed incorrect path to the test assets --- .../BlockAccessToSensitiveDirectories.php | 33 ------------------- tests/Feature/BlockAccessToHtaccessTest.php | 2 +- .../BlockAccessToSensitiveDirectoriesTest.php | 2 +- .../BlockAccessToSensitiveFilesTest.php | 2 +- 4 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 src/SubCommands/BlockAccessToSensitiveDirectories.php diff --git a/src/SubCommands/BlockAccessToSensitiveDirectories.php b/src/SubCommands/BlockAccessToSensitiveDirectories.php deleted file mode 100644 index dc1b2b2..0000000 --- a/src/SubCommands/BlockAccessToSensitiveDirectories.php +++ /dev/null @@ -1,33 +0,0 @@ -commandArguments['directories'] ?? $this->sensitiveDirectories; - if (!empty($directories)) { - $directories = explode(',', $directories); - $directories = array_map('trim', $directories); - $directories_array = []; - - return [ - ['directories' => implode('|', array_map('preg_quote', $directories))] - ]; - } - - return []; - } -} \ No newline at end of file diff --git a/tests/Feature/BlockAccessToHtaccessTest.php b/tests/Feature/BlockAccessToHtaccessTest.php index 95ca187..0a5be8a 100644 --- a/tests/Feature/BlockAccessToHtaccessTest.php +++ b/tests/Feature/BlockAccessToHtaccessTest.php @@ -15,7 +15,7 @@ public function setUp(): void{ $command = new BlockAccessToHtaccess($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingNginxConfigFile() : void { diff --git a/tests/Feature/BlockAccessToSensitiveDirectoriesTest.php b/tests/Feature/BlockAccessToSensitiveDirectoriesTest.php index f2afc5a..1189a18 100644 --- a/tests/Feature/BlockAccessToSensitiveDirectoriesTest.php +++ b/tests/Feature/BlockAccessToSensitiveDirectoriesTest.php @@ -15,7 +15,7 @@ public function setUp(): void{ $command = new BlockAccessToSensitiveDirectories($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingGitDirectory() : void { diff --git a/tests/Feature/BlockAccessToSensitiveFilesTest.php b/tests/Feature/BlockAccessToSensitiveFilesTest.php index 38f68bb..246f6dc 100644 --- a/tests/Feature/BlockAccessToSensitiveFilesTest.php +++ b/tests/Feature/BlockAccessToSensitiveFilesTest.php @@ -15,7 +15,7 @@ public function setUp(): void { $command = new BlockAccessToSensitiveFiles($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingReadmeFiles() : void { From 536d03eabd3e45b455f0ec4d6cdab904ae2e18ca Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:31:57 +0100 Subject: [PATCH 10/14] fix: Fixed an issue that caused incorrect rules to be applied for certain files --- .../BlockAccessToSensitiveFiles.php | 29 ------------------- .../block_access_to_sensitive_files.tpl | 25 +++++++++++++++- .../nginx/block_access_to_sensitive_files.tpl | 18 +++++++++++- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/SubCommands/BlockAccessToSensitiveFiles.php b/src/SubCommands/BlockAccessToSensitiveFiles.php index 6e78477..13e30e5 100644 --- a/src/SubCommands/BlockAccessToSensitiveFiles.php +++ b/src/SubCommands/BlockAccessToSensitiveFiles.php @@ -7,33 +7,4 @@ class BlockAccessToSensitiveFiles extends SubCommand { public string $ruleName = 'BLOCK ACCESS TO SENSITIVE FILES'; public string $successMessage = 'Block Access to Sensitive Files rule has been deployed.'; public string $removalMessage= 'Block Access to Sensitive Files rule has been removed.'; - - /** - * @var string A list of files that should be protected by default - */ - private string $sensitiveFiles = 'readme.html, readme.txt, wp-config.php, wp-admin/install.php'; - - /** - * @return array - */ - public function getTemplateVars(): array { - $files = $this->commandArguments['files'] ?? $this->sensitiveFiles; - - if (!empty($files)) { - $files = explode(',', $files); - $files = array_map('trim', $files); - $files_array = []; - - foreach ($files as $key => $value) { - $file = (isset($this->commandArguments['server']) && $this->commandArguments['server'] === 'nginx') ? - preg_quote($value) : $value; - - $files_array[] = ['file' => $file]; - } - - return $files_array; - } - - return []; - } } \ No newline at end of file diff --git a/src/Templates/apache/block_access_to_sensitive_files.tpl b/src/Templates/apache/block_access_to_sensitive_files.tpl index f38d322..16d2b40 100644 --- a/src/Templates/apache/block_access_to_sensitive_files.tpl +++ b/src/Templates/apache/block_access_to_sensitive_files.tpl @@ -1,4 +1,4 @@ - + Require all denied @@ -7,3 +7,26 @@ Deny from all + + + Require all denied + + + Order allow,deny + Deny from all + + + + + Require all denied + + + Order allow,deny + Deny from all + + + + RewriteEngine On + RewriteRule ^wp-admin/install\.php$ - [F] + RewriteRule ^wp-admin/upgrade\.php$ - [F] + \ No newline at end of file diff --git a/src/Templates/nginx/block_access_to_sensitive_files.tpl b/src/Templates/nginx/block_access_to_sensitive_files.tpl index 522e048..7f75180 100644 --- a/src/Templates/nginx/block_access_to_sensitive_files.tpl +++ b/src/Templates/nginx/block_access_to_sensitive_files.tpl @@ -1,3 +1,19 @@ -location ~ /{{file}}$ { +location = /wp-admin/install.php { deny all; } + +location = /wp-admin/upgrade.php { + deny all; +} + +location ~ /readme\.html$ { + deny all; +} + +location ~ /readme\.txt$ { + deny all; +} + +location ~ /wp-config.php$ { + deny all; +} \ No newline at end of file From dc4be11a6f3dda1395d830a22b3b34ec32ff1f86 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:32:24 +0100 Subject: [PATCH 11/14] feat: Migrated custom files blocking to a separate class --- .../BlockAccessToCustomSensitiveFiles.php | 34 +++++++++++++++++++ ...block_access_to_custom_sensitive_files.tpl | 9 +++++ ...block_access_to_custom_sensitive_files.tpl | 3 ++ 3 files changed, 46 insertions(+) create mode 100644 src/SubCommands/BlockAccessToCustomSensitiveFiles.php create mode 100644 src/Templates/apache/block_access_to_custom_sensitive_files.tpl create mode 100644 src/Templates/nginx/block_access_to_custom_sensitive_files.tpl diff --git a/src/SubCommands/BlockAccessToCustomSensitiveFiles.php b/src/SubCommands/BlockAccessToCustomSensitiveFiles.php new file mode 100644 index 0000000..1f6b85a --- /dev/null +++ b/src/SubCommands/BlockAccessToCustomSensitiveFiles.php @@ -0,0 +1,34 @@ +commandArguments['files']; + + if(!empty($files)) { + $files = explode(',', $files); + $files = array_map('trim', $files); + $files_array = []; + + foreach ($files as $key => $value) { + $file = (isset($this->commandArguments['server']) && $this->commandArguments['server'] === 'nginx') ? + preg_quote($value) : $value; + + $files_array[] = ['file' => $file]; + } + + return $files_array; + } + + return []; + } +} \ No newline at end of file diff --git a/src/Templates/apache/block_access_to_custom_sensitive_files.tpl b/src/Templates/apache/block_access_to_custom_sensitive_files.tpl new file mode 100644 index 0000000..f38d322 --- /dev/null +++ b/src/Templates/apache/block_access_to_custom_sensitive_files.tpl @@ -0,0 +1,9 @@ + + + Require all denied + + + Order allow,deny + Deny from all + + diff --git a/src/Templates/nginx/block_access_to_custom_sensitive_files.tpl b/src/Templates/nginx/block_access_to_custom_sensitive_files.tpl new file mode 100644 index 0000000..522e048 --- /dev/null +++ b/src/Templates/nginx/block_access_to_custom_sensitive_files.tpl @@ -0,0 +1,3 @@ +location ~ /{{file}}$ { + deny all; +} From 152c957c4b3c7a4638e74e0cb1f17ab9f3720898 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:34:34 +0100 Subject: [PATCH 12/14] feat: Custom files blocking is now handled by a separate class --- src/SecureCommand.php | 14 ++++++-- .../BlockAccessToSensitiveDirectories.php | 33 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/SubCommands/BlockAccessToSensitiveDirectories.php diff --git a/src/SecureCommand.php b/src/SecureCommand.php index 1a657ec..433e879 100644 --- a/src/SecureCommand.php +++ b/src/SecureCommand.php @@ -4,6 +4,7 @@ use WP_CLI; use WP_CLI_Command; +use WP_CLI_Secure\SubCommands\BlockAccessToCustomSensitiveFiles; use WP_CLI_Secure\SubCommands\BlockAccessToHtaccess; use WP_CLI_Secure\SubCommands\BlockAccessToSensitiveDirectories; use WP_CLI_Secure\SubCommands\BlockAccessToSensitiveFiles; @@ -215,12 +216,12 @@ public function block_access($args, $assoc_args): void { WP_CLI::error(sprintf('Invalid block part "%s" was provided. Allowed values are ' . implode(', ', $allowedSubArguments), $blockPart)); } - if(in_array($blockPart, ['all', 'custom', 'sensitive-files'])) { + if(in_array($blockPart, ['all', 'sensitive-files'])) { WP_CLI::debug('Blocking access to the sensitive files.', 'secure'); (new BlockAccessToSensitiveFiles($assoc_args))->output(); } - if(in_array($blockPart, ['all', 'custom', 'sensitive-directories'])) { + if(in_array($blockPart, ['all', 'sensitive-directories'])) { WP_CLI::debug('Blocking access to the directories.', 'secure'); (new BlockAccessToSensitiveDirectories($assoc_args))->output(); } @@ -234,6 +235,15 @@ public function block_access($args, $assoc_args): void { WP_CLI::debug('Blocking access to the xmlrpc.', 'secure'); (new BlockAccessToXmlRpc($assoc_args))->output(); } + + //Custom files and directories blocking + if($blockPart === 'custom' && isset($assoc_args['files'])) { + (new BlockAccessToCustomSensitiveFiles($assoc_args))->output(); + } + + if($blockPart === 'custom' && isset($assoc_args['directories'])) { + (new BlockAccessToSensitiveDirectories($assoc_args))->output(); + } } /** diff --git a/src/SubCommands/BlockAccessToSensitiveDirectories.php b/src/SubCommands/BlockAccessToSensitiveDirectories.php new file mode 100644 index 0000000..dc1b2b2 --- /dev/null +++ b/src/SubCommands/BlockAccessToSensitiveDirectories.php @@ -0,0 +1,33 @@ +commandArguments['directories'] ?? $this->sensitiveDirectories; + if (!empty($directories)) { + $directories = explode(',', $directories); + $directories = array_map('trim', $directories); + $directories_array = []; + + return [ + ['directories' => implode('|', array_map('preg_quote', $directories))] + ]; + } + + return []; + } +} \ No newline at end of file From f892615fa081a9a7bbc2b5d603622c9b20b93770 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:37:08 +0100 Subject: [PATCH 13/14] fix: Fixed incorrect path for calling nginx reload command --- tests/Feature/BlockAccessToXmlRpcTest.php | 2 +- tests/Feature/BlockAuthorScanningTest.php | 2 +- tests/Feature/BlockPhpExecutionInPluginsTest.php | 2 +- tests/Feature/BlockPhpExecutionInThemesTest.php | 2 +- tests/Feature/BlockPhpExecutionInUploadsTest.php | 2 +- tests/Feature/BlockPhpExecutionInWpIncludesTest.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/Feature/BlockAccessToXmlRpcTest.php b/tests/Feature/BlockAccessToXmlRpcTest.php index 2c917bb..a102e4a 100644 --- a/tests/Feature/BlockAccessToXmlRpcTest.php +++ b/tests/Feature/BlockAccessToXmlRpcTest.php @@ -15,7 +15,7 @@ public function setUp(): void { $command = new BlockAccessToXmlRpc($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingXmlRpcFile() : void { diff --git a/tests/Feature/BlockAuthorScanningTest.php b/tests/Feature/BlockAuthorScanningTest.php index 6867bac..c9cedb0 100644 --- a/tests/Feature/BlockAuthorScanningTest.php +++ b/tests/Feature/BlockAuthorScanningTest.php @@ -12,7 +12,7 @@ public function setUp(): void { $command = new BlockAuthorScanning($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnApacheWhenAccessingPhpFilesInWpIncludesDirectory() : void { diff --git a/tests/Feature/BlockPhpExecutionInPluginsTest.php b/tests/Feature/BlockPhpExecutionInPluginsTest.php index cb1a5d0..5cea692 100644 --- a/tests/Feature/BlockPhpExecutionInPluginsTest.php +++ b/tests/Feature/BlockPhpExecutionInPluginsTest.php @@ -15,7 +15,7 @@ public function setUp(): void { $command = new BlockPhpExecutionInPlugins($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingPhpFilesInPlugins() : void { diff --git a/tests/Feature/BlockPhpExecutionInThemesTest.php b/tests/Feature/BlockPhpExecutionInThemesTest.php index 001f962..c6ba7dc 100644 --- a/tests/Feature/BlockPhpExecutionInThemesTest.php +++ b/tests/Feature/BlockPhpExecutionInThemesTest.php @@ -15,7 +15,7 @@ public function setUp(): void { $command = new BlockPhpExecutionInThemes($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingPhpFilesInThemesDirectory() : void { diff --git a/tests/Feature/BlockPhpExecutionInUploadsTest.php b/tests/Feature/BlockPhpExecutionInUploadsTest.php index bdaca27..db99cde 100644 --- a/tests/Feature/BlockPhpExecutionInUploadsTest.php +++ b/tests/Feature/BlockPhpExecutionInUploadsTest.php @@ -15,7 +15,7 @@ public function setUp(): void { $command = new BlockPhpExecutionInUploads($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingPhpFilesInUploadsDirectory() : void { diff --git a/tests/Feature/BlockPhpExecutionInWpIncludesTest.php b/tests/Feature/BlockPhpExecutionInWpIncludesTest.php index 7576917..20ce4c5 100644 --- a/tests/Feature/BlockPhpExecutionInWpIncludesTest.php +++ b/tests/Feature/BlockPhpExecutionInWpIncludesTest.php @@ -15,7 +15,7 @@ public function setUp(): void { $command = new BlockPhpExecutionInWpIncludes($this->apacheAssocArgs); $command->output(); - exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); + exec('cd ' . getcwd() . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload'); } public function testItWillReturnHttp403OnNginxWhenAccessingPhpFilesInWpIncludesDirectory() : void { From 1dcfad140f5ce553cf408ec13064d85efd1d1a25 Mon Sep 17 00:00:00 2001 From: Igor Hrcek Date: Sun, 20 Mar 2022 22:59:20 +0100 Subject: [PATCH 14/14] docs: Updated documentation for all commands --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 91ac688..74a0160 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Removes all security rules. wp secure flush ``` -### Block access to sensitive files and directories +### Block the access to sensitive files and directories ```bash wp secure block-access ``` -Blocks direct access to sensitive files and directories: +By default, this command blocks the direct access to sensitive files and directories: `readme.txt`, `readme.html`, `xmlrpc.php`, `wp-config.php`, `wp-admin/install.php`, `wp-admin/upgrade.php`, `.git`, `svn`, `cache` and `vendors` Possible options are: @@ -33,6 +33,7 @@ Possible options are: - sensitive-directories - xmlrpc - htaccess +- custom - all (does all the above) Examples: @@ -45,6 +46,18 @@ wp secure block-access htaccess wp secure block-access all ``` +However, you can also block custom files and/or folders of your choice. To do that you should use `custom` argument +and pass one of two additional options `--files` and/or `--directories`. + +If you want to block custom files, make sure that you pass only file names, not a full file paths. + +Examples: + +````bash +wp secure block-access custom --files=dump.sql,phpinfo.php,adminer.php +wp secure block-access custom --directories=wp-content/mu-plugins +```` + ### Block Author Scanning ```bash @@ -91,7 +104,7 @@ wp secure disable-directory-browsing Disables directory browsing. -By default when your web server does not find an index file (i.e. a file like index.php or index.html), it +By default, when your web server does not find an index file (i.e. a file like index.php or index.html), it automatically displays an index page showing the contents of the directory. This could make your site vulnerable to hack attacks by revealing important information needed to exploit a vulnerability in a WordPress plugin, theme, or your server in general. @@ -110,6 +123,31 @@ This makes it easier for attackers to change files on the server using a web bro wp secure disable-file-editor ``` +### Fix file and directory permissions + +```bash +wp secure fix-permissions +``` + +Use this command to verify that the permissions of all files and directories are set according the WordPress recommendations. +This command will set **0666** to all files and **0755** to all folders inside WordPress installation. + +**IMPORTANT: Don't use this command if you don't know what you are doing here!** + +### Check the integrity of WordPress files + +Downloads MD5 checksums for the current version from WordPress.org, and compares those checksums against the currently +installed files. + +It also returns a list of files that shouldn't be part of default WordPress installation, which can be very useful when you are +looking for a possible injected files. + +Examples: + +```bash +wp secure integrity-scan +``` + ## Global options ### Remove single security rule @@ -143,7 +181,7 @@ wp secure block-access htaccess --file-path=/home/user/mysite.com/.htaccess ``` ## Important Note for nginx users -nginx rules are stored in the `nginx.conf` file. However, for rules to actually work, you need to manually include this file in your vhost configuration and then restart nginx server: +The nginx rules are stored in the `nginx.conf` file. However, for rules to actually work, you need to manually include this file in your vhost configuration and then restart nginx server: ``` systemctl restart nginx ```