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']],