From acf91933f96e1e8a561136b2e41072b466e11ec2 Mon Sep 17 00:00:00 2001 From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org> Date: Wed, 24 Jun 2015 10:15:36 +0100 Subject: [PATCH] Issue #2508666 by alexpott, pwolanin, benjy: Drupal 8 .htaccess rule to prevent php file access can be easily bypassed --- .htaccess | 6 +- .../system/src/Tests/System/HtaccessTest.php | 94 ++++++++++++++++--- .../HtaccessTest/access_test.module.bak | 0 .../HtaccessTest/access_test.module.orig | 0 .../HtaccessTest/access_test.module.save | 0 .../HtaccessTest/access_test.module.swo | 0 .../HtaccessTest/access_test.module.swp | 0 .../fixtures/HtaccessTest/access_test.module~ | 0 .../HtaccessTest/access_test.php-info.txt | 0 .../fixtures/HtaccessTest/access_test.php.bak | 0 .../HtaccessTest/access_test.php.orig | 0 .../HtaccessTest/access_test.php.save | 0 .../fixtures/HtaccessTest/access_test.php.swo | 0 .../fixtures/HtaccessTest/access_test.php.swp | 0 .../fixtures/HtaccessTest/access_test.php~ | 0 15 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.module.bak create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.module.orig create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.module.save create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.module.swo create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.module.swp create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.module~ create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.php-info.txt create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.php.bak create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.php.orig create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.php.save create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.php.swo create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.php.swp create mode 100644 core/modules/system/tests/fixtures/HtaccessTest/access_test.php~ diff --git a/.htaccess b/.htaccess index 07cf7e45c630..af418c46d96c 100644 --- a/.htaccess +++ b/.htaccess @@ -3,7 +3,7 @@ # # Protect files and directories from prying eyes. -<FilesMatch "\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$"> +<FilesMatch "\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$"> <IfModule mod_authz_core.c> Require all denied </IfModule> @@ -139,14 +139,14 @@ AddEncoding gzip svgz # Allow access to PHP files in /core (like authorize.php or install.php): RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$ # Allow access to test-specific PHP files: - RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?.php$ + RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?.php # Allow access to Statistics module's custom front controller. # Copy and adapt this rule to directly execute PHP files in contributed or # custom modules or to run another PHP application in the same directory. RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics.php$ # Deny access to any other PHP files that do not match the rules above. # Specifically, disallow autoload.php from being served directly. - RewriteRule "^(.+/.*|autoload)\.php$" - [F] + RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F] # Rules to correctly serve gzip compressed CSS and JS files. # Requires both mod_rewrite and mod_headers to be enabled. diff --git a/core/modules/system/src/Tests/System/HtaccessTest.php b/core/modules/system/src/Tests/System/HtaccessTest.php index 8a44715b89c8..291dfeecea23 100644 --- a/core/modules/system/src/Tests/System/HtaccessTest.php +++ b/core/modules/system/src/Tests/System/HtaccessTest.php @@ -15,20 +15,44 @@ * @group system */ class HtaccessTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('node', 'path'); + /** * Get an array of file paths for access testing. * - * @return array - * An array of file paths to be access-tested. + * @return int[] + * An array keyed by file paths. Each value is the expected response code, + * for example, 200 or 403. */ protected function getProtectedFiles() { $path = drupal_get_path('module', 'system') . '/tests/fixtures/HtaccessTest'; - $file_exts = [ + + // Tests the FilesMatch directive which denies access to certain file + // extensions. + $file_exts_to_deny = [ 'engine', 'inc', 'install', 'make', 'module', + 'module~', + 'module.bak', + 'module.orig', + 'module.save', + 'module.swo', + 'module.swp', + 'php~', + 'php.bak', + 'php.orig', + 'php.save', + 'php.swo', + 'php.swp', 'profile', 'po', 'sh', @@ -40,13 +64,28 @@ protected function getProtectedFiles() { 'yml', ]; - foreach ($file_exts as $file_ext) { - $file_paths[] = "$path/access_test.$file_ext"; + foreach ($file_exts_to_deny as $file_ext) { + $file_paths["$path/access_test.$file_ext"] = 403; } + // Tests the .htaccess file in core/vendor and created by a Composer script. // Try and access a non PHP file in the vendor directory. - $file_paths[] = 'core/vendor/composer/installed.json'; + // @see Drupal\\Core\\Composer\\Composer::ensureHtaccess + $file_paths['core/vendor/composer/installed.json'] = 403; + + // Tests the rewrite conditions and rule that denies access to php files. + $file_paths['core/lib/Drupal.php'] = 403; + $file_paths['core/vendor/autoload.php'] = 403; + $file_paths['autoload.php'] = 403; + + // Test extensions that should be permitted. + $file_exts_to_allow = [ + 'php-info.txt' + ]; + foreach ($file_exts_to_allow as $file_ext) { + $file_paths["$path/access_test.$file_ext"] = 200; + } return $file_paths; } @@ -54,21 +93,52 @@ protected function getProtectedFiles() { * Iterates over protected files and calls assertNoFileAccess(). */ public function testFileAccess() { - foreach ($this->getProtectedFiles() as $file) { - $this->assertNoFileAccess($file); + foreach ($this->getProtectedFiles() as $file => $response_code) { + $this->assertFileAccess($file, $response_code); } + + // Test that adding "/1" to a .php URL does not make it accessible. + $this->drupalGet('core/lib/Drupal.php/1'); + $this->assertResponse(403, "Access to core/lib/Drupal.php/1 is denied."); + + // Test that is it possible to have path aliases containing .php. + $type = $this->drupalCreateContentType(); + + // Create an node aliased to test.php. + $node = $this->drupalCreateNode([ + 'title' => 'This is a node', + 'type' => $type->id(), + 'path' => 'test.php' + ]); + $node->save(); + $this->drupalGet('test.php'); + $this->assertResponse(200); + $this->assertText('This is a node'); + + // Update node's alias to test.php/test. + $node->path = 'test.php/test'; + $node->save(); + $this->drupalGet('test.php/test'); + $this->assertResponse(200); + $this->assertText('This is a node'); } /** - * Asserts that a file exists but not accessible via HTTP. + * Asserts that a file exists and requesting it returns a specific response. * * @param string $path * Path to file. Without leading slash. + * @param int $response_code + * The expected response code. For example: 200, 403 or 404. + * + * @return bool + * TRUE if the assertion succeeded, FALSE otherwise. */ - protected function assertNoFileAccess($path) { - $this->assertTrue(file_exists(\Drupal::root() . '/' . $path)); + protected function assertFileAccess($path, $response_code) { + $result = $this->assertTrue(file_exists(\Drupal::root() . '/' . $path), "The file $path exists."); $this->drupalGet($path); - $this->assertResponse(403); + $result = $result && $this->assertResponse($response_code, "Response code to $path is $response_code."); + return $result; } /** diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.bak b/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.bak new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.orig b/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.orig new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.save b/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.save new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.swo b/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.swo new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.swp b/core/modules/system/tests/fixtures/HtaccessTest/access_test.module.swp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.module~ b/core/modules/system/tests/fixtures/HtaccessTest/access_test.module~ new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.php-info.txt b/core/modules/system/tests/fixtures/HtaccessTest/access_test.php-info.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.bak b/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.bak new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.orig b/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.orig new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.save b/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.save new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.swo b/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.swo new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.swp b/core/modules/system/tests/fixtures/HtaccessTest/access_test.php.swp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/modules/system/tests/fixtures/HtaccessTest/access_test.php~ b/core/modules/system/tests/fixtures/HtaccessTest/access_test.php~ new file mode 100644 index 000000000000..e69de29bb2d1 -- GitLab