diff --git a/docs/install.md b/docs/install.md index bc8158d11..0854483a9 100644 --- a/docs/install.md +++ b/docs/install.md @@ -15,9 +15,6 @@ These instructions assume that you have already [installed the CodeIgniter 4 app > **Note** > CodeIgniter Shield requires Codeigniter v4.2.3 or later. -> **Note** -> You must set ``Config\Security::$csrfProtection`` to `'session'` (or set `security.csrfProtection = session` in your `.env` file) for security reasons, if you use Session Authenticator. - Installation is done through [Composer](https://getcomposer.org). The example assumes you have it installed globally. If you have it installed as a phar, or othewise you will need to adjust the way you call composer itself. @@ -89,7 +86,7 @@ If you get `Specified key was too long` error: ### Command Setup -1. Run the following command. This command handles steps 1-3 of *Manual Setup* and runs the migrations. +1. Run the following command. This command handles steps 1-4 of *Manual Setup* and runs the migrations. ``` > php spark shield:setup @@ -137,6 +134,8 @@ This requires that all of your controllers extend the `BaseController`, but that service('auth')->routes($routes); ``` +4. **Security Setup** Set `Config\Security::$csrfProtection` to `'session'` (or set `security.csrfProtection = session` in your `.env` file) for security reasons, if you use Session Authenticator. + ## Controller Filters Shield provides 4 [Controller Filters](https://codeigniter.com/user_guide/incoming/filters.html) you can diff --git a/src/Commands/Setup.php b/src/Commands/Setup.php index 6ade052e8..1b8bea0b8 100644 --- a/src/Commands/Setup.php +++ b/src/Commands/Setup.php @@ -87,6 +87,8 @@ private function publishConfig(): void $this->setupHelper(); $this->setupRoutes(); + $this->setSecurityCSRF(); + $this->runMigrations(); } @@ -258,6 +260,42 @@ private function setupRoutes(): void $this->add($file, $check, $pattern, $replace); } + /** + * @see https://github.com/codeigniter4/shield/security/advisories/GHSA-5hm8-vh6r-2cjq + */ + private function setSecurityCSRF(): void + { + $file = 'Config/Security.php'; + $replaces = [ + 'public $csrfProtection = \'cookie\';' => 'public $csrfProtection = \'session\';', + ]; + + $path = $this->distPath . $file; + $cleanPath = clean_path($path); + + if (! is_file($path)) { + CLI::error(" Not found file '{$cleanPath}'."); + + return; + } + + $content = file_get_contents($path); + $output = $this->replacer->replace($content, $replaces); + + // check $csrfProtection = 'session' + if ($output === $content) { + CLI::write(CLI::color(' Security Setup: ', 'green') . 'Everything is fine.'); + + return; + } + + if (write_file($path, $output)) { + CLI::write(CLI::color(' Updated: ', 'green') . "We have updated file '{$cleanPath}' for security reasons."); + } else { + CLI::error(" Error updating file '{$cleanPath}'."); + } + } + private function runMigrations(): void { if ( diff --git a/tests/Commands/SetupTest.php b/tests/Commands/SetupTest.php index 496424bee..a0b2659f9 100644 --- a/tests/Commands/SetupTest.php +++ b/tests/Commands/SetupTest.php @@ -60,13 +60,17 @@ public function testRun(): void $routes = file_get_contents($appFolder . 'Config/Routes.php'); $this->assertStringContainsString('service(\'auth\')->routes($routes);', $routes); + $security = file_get_contents($appFolder . 'Config/Security.php'); + $this->assertStringContainsString('public $csrfProtection = \'session\';', $security); + $result = str_replace(["\033[0;32m", "\033[0m"], '', CITestStreamFilter::$buffer); $this->assertStringContainsString( ' Created: vfs://root/Config/Auth.php Created: vfs://root/Config/AuthGroups.php Updated: vfs://root/Controllers/BaseController.php - Updated: vfs://root/Config/Routes.php', + Updated: vfs://root/Config/Routes.php + Updated: We have updated file \'vfs://root/Config/Security.php\' for security reasons.', $result ); $this->assertStringContainsString(