diff --git a/.tools/psalm/baseline.xml b/.tools/psalm/baseline.xml index 794d0e6271..528a89beb3 100644 --- a/.tools/psalm/baseline.xml +++ b/.tools/psalm/baseline.xml @@ -1256,13 +1256,6 @@ - - - - - - - getProperty('allowed_mime_types', [])]]> getProperty('blocked_extensions')]]> diff --git a/redaxo/src/addons/mediapool/lib/mediapool.php b/redaxo/src/addons/mediapool/lib/mediapool.php index 72ea1d44c0..53783664d1 100644 --- a/redaxo/src/addons/mediapool/lib/mediapool.php +++ b/redaxo/src/addons/mediapool/lib/mediapool.php @@ -114,13 +114,13 @@ public static function isAllowedExtension(string $filename, array $args = []): b return false; } + // A blocked extension must not appear as a dot-separated segment anywhere in the filename, + // to prevent double/multi extension vulnerabilities: some webspaces execute a file named + // e.g. `foo.php.txt` or `foo.php.any.jpg` as php. Matching whole segments (instead of a + // substring) avoids false positives like `foo.json` (contains `.js`) or `js_datei.txt`. $blockedExtensions = self::getBlockedExtensions(); - foreach ($blockedExtensions as $blockedExtension) { - // $blockedExtensions extensions are not allowed within filenames, to prevent double extension vulnerabilities: - // -> some webspaces execute files named file.php.txt as php - if (str_ends_with($filename, '.' . $blockedExtension) // Prüfe ob der String mit der verbotenen Endung endet - || str_ends_with($filename, '.' . $blockedExtension . '.' . $fileExt) // prüfe ob es keine doppelte Endung der Form *.php.ext gibt - ) { + foreach (explode('.', mb_strtolower($filename)) as $segment) { + if (in_array($segment, $blockedExtensions, true)) { return false; } } diff --git a/redaxo/src/addons/mediapool/tests/mediapool_test.php b/redaxo/src/addons/mediapool/tests/mediapool_test.php index 03b84dee10..80e502c530 100644 --- a/redaxo/src/addons/mediapool/tests/mediapool_test.php +++ b/redaxo/src/addons/mediapool/tests/mediapool_test.php @@ -24,9 +24,15 @@ public static function provideIsAllowedExtension(): array [false, '.htaccess'], [false, '.htpasswd'], [false, 'foo.js.txt'], + [false, 'foo.js.any.txt'], + [false, 'shell.php.any.jpg'], + [false, 'shell.PHP.any.JPG'], + [false, 'shell.phtml.any.jpg'], [true, 'js_datei.txt'], [true, 'foo.json'], + [true, 'foo.any.json'], [true, 'php_logo.jpg'], + [true, 'php_logo.any.jpg'], [true, 'foo.bar.png', ['types' => 'jpg,png,gif']], [false, 'foo.bar.txt', ['types' => 'jpg,png,gif']], [false, 'foo.bar.php', ['types' => 'jpg,png,gif,php']],