diff --git a/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php b/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php
index c01d7f1e3e5fab845fae1b922b2828de6bfb5bac..82d68b7bac40bac7c4ff0babb9315f90bb7b061a 100644
--- a/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php
+++ b/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php
@@ -165,6 +165,12 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream
         }
         $absoluteBasePath = $this->canonicalizeAndCheckFilePath($absoluteBasePath);
         $absoluteBasePath = rtrim($absoluteBasePath, '/') . '/';
+        if (!$this->isAllowedAbsolutePath($absoluteBasePath)) {
+            throw new InvalidConfigurationException(
+                'Base path "' . $absoluteBasePath . '" is not within the allowed project root path or allowed lockRootPath.',
+                1704807715
+            );
+        }
         if (!is_dir($absoluteBasePath)) {
             throw new InvalidConfigurationException(
                 'Base path "' . $absoluteBasePath . '" does not exist or is no directory.',
@@ -1464,4 +1470,13 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream
 
         return '';
     }
+
+    /**
+     * Wrapper for `GeneralUtility::isAllowedAbsPath`, which implicitly invokes
+     * `GeneralUtility::validPathStr` (like in `parent::isPathValid`).
+     */
+    protected function isAllowedAbsolutePath(string $path): bool
+    {
+        return GeneralUtility::isAllowedAbsPath($path);
+    }
 }
diff --git a/typo3/sysext/core/Classes/Utility/GeneralUtility.php b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
index 2aae2f2b43be1e02d742f09f935327a3df741a2f..fa63fbdedc7ae69ac0389a1b7422715537862b23 100644
--- a/typo3/sysext/core/Classes/Utility/GeneralUtility.php
+++ b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
@@ -2659,12 +2659,11 @@ class GeneralUtility
         if (substr($path, 0, 6) === 'vfs://') {
             return true;
         }
-        $lockRootPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] ?? '';
         return PathUtility::isAbsolutePath($path) && static::validPathStr($path)
             && (
                 str_starts_with($path, Environment::getProjectPath())
                 || str_starts_with($path, Environment::getPublicPath())
-                || ($lockRootPath && str_starts_with($path, $lockRootPath))
+                || PathUtility::isAllowedAdditionalPath($path)
             );
     }
 
diff --git a/typo3/sysext/core/Classes/Utility/PathUtility.php b/typo3/sysext/core/Classes/Utility/PathUtility.php
index 31a01cfb41afadcdca69eb2ef6b9c079fdc07586..d676ea04274c415136a85867321492b675aca06b 100644
--- a/typo3/sysext/core/Classes/Utility/PathUtility.php
+++ b/typo3/sysext/core/Classes/Utility/PathUtility.php
@@ -446,4 +446,33 @@ class PathUtility
     {
         return str_starts_with($path, '//') || strpos($path, '://') > 0;
     }
+
+    /**
+     * Evaluates a given path against the optional settings in `$GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']`.
+     * Albeit the name `BE/lockRootPath` is misleading, this setting was and is used in general and is not limited
+     * to the backend-scope. The setting actually allows defining additional paths, besides the project root path.
+     *
+     * @param string $path Absolute path to a file or directory
+     */
+    public static function isAllowedAdditionalPath(string $path): bool
+    {
+        // ensure the submitted path ends with a string, even for a file
+        $path = self::sanitizeTrailingSeparator($path);
+        $allowedPaths = $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] ?? [];
+        if (is_string($allowedPaths)) {
+            // The setting was a string before and is now an array
+            // For compatibility reasons, we cast a string to an array here for now
+            $allowedPaths = [$allowedPaths];
+        }
+        if (!is_array($allowedPaths)) {
+            throw new \RuntimeException('$GLOBALS[\'TYPO3_CONF_VARS\'][\'BE\'][\'lockRootPath\'] is expected to be an array.', 1707408379);
+        }
+        foreach ($allowedPaths as $allowedPath) {
+            $allowedPath = trim($allowedPath);
+            if ($allowedPath !== '' && str_starts_with($path, self::sanitizeTrailingSeparator($allowedPath))) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml b/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml
index 869dac0a84a2cb4984ffab83820b68cbea5c2af9..a6ae8acf1cc29afc994962e811d706a96dbfc311 100644
--- a/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml
+++ b/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml
@@ -246,8 +246,8 @@ BE:
             type: text
             description: 'Path to the primary directory of files for editors. This is relative to the public web dir, DefaultStorage will be created with that configuration, do not access manually but via <code>\TYPO3\CMS\Core\Resource\ResourceFactory::getDefaultStorage().</code>'
         lockRootPath:
-            type: text
-            description: 'This path is used to evaluate if paths outside of public web path should be allowed. Ending slash required!'
+            type: array
+            description: 'List of absolute root path prefixes to be allowed for file operations (including FAL storages). The project root path is allowed in any case and does not need to be defined here. Ending slashes are enforced!'
         userHomePath:
             type: text
             description: 'Combined folder identifier of the directory where TYPO3 backend-users have their home-dirs. A combined folder identifier looks like this: [storageUid]:[folderIdentifier]. Eg. <code>2:users/</code>. A home for backend user 2 would be: <code>2:users/2/</code>. Ending slash required!'
diff --git a/typo3/sysext/core/Documentation/Changelog/11.5.x/Important-102800-FileAbstractionLayerEnforcesAbsolutePathsToMatchProjectRootOrLockRootPath.rst b/typo3/sysext/core/Documentation/Changelog/11.5.x/Important-102800-FileAbstractionLayerEnforcesAbsolutePathsToMatchProjectRootOrLockRootPath.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0eff41bb18d821260636aa1f400f196549b82c78
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/11.5.x/Important-102800-FileAbstractionLayerEnforcesAbsolutePathsToMatchProjectRootOrLockRootPath.rst
@@ -0,0 +1,39 @@
+.. include:: /Includes.rst.txt
+
+.. _important-102800-1707409544:
+
+=========================================================================================================
+Important: #102800 - File Abstraction Layer enforces absolute paths to match project root or lockRootPath
+=========================================================================================================
+
+See :issue:`102800`
+
+Description
+===========
+
+
+The File Abstraction Layer Local Driver has been adapted to verify whether a
+given absolute file path is allowed in order to prevent access to files outside
+the project root or to the additional root path restrictions defined in
+:php:`$GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']`.
+
+The option :php:`$GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']` has been
+extended to support an array of root path prefixes to allow for multiple storages
+to be listed. Beware that trailing slashes are enforced automatically.
+
+It is suggested to use the new array-based syntax, which will be applied automatically
+once this setting is updated via Install Tool Configuration Wizard:
+
+..  code-block:: php
+
+    // Before
+    $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] = '/var/extra-storage';
+
+    // After
+    $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] =  [
+        '/var/extra-storage1/',
+        '/var/extra-storage2/',
+    ];
+
+
+.. index:: FAL, LocalConfiguration, ext:core
diff --git a/typo3/sysext/core/Tests/Unit/Utility/PathUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/PathUtilityTest.php
index 8eb78ff01070286ed171f05207b66b67ff709cdb..5e977c4b650f1406cd6642830e21705c024529fe 100644
--- a/typo3/sysext/core/Tests/Unit/Utility/PathUtilityTest.php
+++ b/typo3/sysext/core/Tests/Unit/Utility/PathUtilityTest.php
@@ -560,4 +560,41 @@ final class PathUtilityTest extends UnitTestCase
     {
         self::assertSame($result, PathUtility::hasProtocolAndScheme($url));
     }
+
+    public static function allowedAdditionalPathsAreEvaluatedDataProvider(): \Generator
+    {
+        // empty settings
+        yield [null, '/var/shared', false];
+        yield ['', '/var/shared', false];
+        yield [' ', '/var/shared', false];
+        yield [[], '/var/shared', false];
+        yield [[''], '/var/shared', false];
+        yield [[' '], '/var/shared', false];
+        // string settings
+        yield ['/var', '/var/shared', true];
+        yield ['/var/shared/', '/var/shared', true];
+        yield ['/var/shared', '/var/shared/', true];
+        yield ['/var/shared/', '/var/shared/', true];
+        yield ['/var/shared/', '/var/shared/file.png', true];
+        yield ['/var/shared/', '/var/shared-secret', false];
+        yield ['/var/shared/', '/var', false];
+        // array settings
+        yield [['/var'], '/var/shared', true];
+        yield [['/var/shared/'], '/var/shared', true];
+        yield [['/var/shared'], '/var/shared/', true];
+        yield [['/var/shared/'], '/var/shared/', true];
+        yield [['/var/shared/'], '/var/shared/file.png', true];
+        yield [['/var/shared/'], '/var/shared-secret', false];
+        yield [['/var/shared/'], '/var', false];
+    }
+
+    /**
+     * @test
+     * @dataProvider allowedAdditionalPathsAreEvaluatedDataProvider
+     */
+    public function allowedAdditionalPathsAreEvaluated(mixed $lockRootPath, string $path, bool $expectation): void
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] = $lockRootPath;
+        self::assertSame($expectation, PathUtility::isAllowedAdditionalPath($path));
+    }
 }
diff --git a/typo3/sysext/filelist/Classes/Controller/FileListController.php b/typo3/sysext/filelist/Classes/Controller/FileListController.php
index ed4370f040af6933d2c76207c47e838c4da7dbec..fd0bd0840082c857d78e31c0c8c335cde24752bb 100644
--- a/typo3/sysext/filelist/Classes/Controller/FileListController.php
+++ b/typo3/sysext/filelist/Classes/Controller/FileListController.php
@@ -43,6 +43,7 @@ use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
 use TYPO3\CMS\Core\Resource\Exception;
+use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
 use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
 use TYPO3\CMS\Core\Resource\Folder;
 use TYPO3\CMS\Core\Resource\ResourceFactory;
@@ -148,7 +149,7 @@ class FileListController implements LoggerAwareInterface
             if ($this->folderObject && !$this->folderObject->getStorage()->isWithinFileMountBoundaries($this->folderObject)) {
                 throw new \RuntimeException('Folder not accessible.', 1430409089);
             }
-        } catch (InsufficientFolderAccessPermissionsException $permissionException) {
+        } catch (FolderDoesNotExistException|InsufficientFolderAccessPermissionsException $permissionException) {
             $this->folderObject = null;
             if ($storage !== null && $storage->getDriverType() === 'Local' && !$storage->isOnline()) {
                 // If the base folder for a local storage does not exists, the storage is marked as offline and the
diff --git a/typo3/sysext/filelist/Resources/Private/Language/locallang_mod_file_list.xlf b/typo3/sysext/filelist/Resources/Private/Language/locallang_mod_file_list.xlf
index 46d36672413337659ef60f4a738f1ab4ed8b1e38..c40683387b0a061b7c5da34ffdf1b4e3933ac9a7 100644
--- a/typo3/sysext/filelist/Resources/Private/Language/locallang_mod_file_list.xlf
+++ b/typo3/sysext/filelist/Resources/Private/Language/locallang_mod_file_list.xlf
@@ -73,10 +73,10 @@
 				<source>You have no access to the folder "%s".</source>
 			</trans-unit>
 			<trans-unit id="localStorageOfflineTitle" resname="localStorageOfflineTitle">
-				<source>Base folder for local storage missing</source>
+				<source>Base folder for local storage missing or not allowed</source>
 			</trans-unit>
 			<trans-unit id="localStorageOfflineMessage" resname="localStorageOfflineMessage">
-				<source>Base folder for local storage does not exists. Verify that the base folder for "%s" exists.</source>
+				<source>Verify that the base folder for the storage "%s" exists and is allowed to be accessed.</source>
 			</trans-unit>
 			<trans-unit id="folderNotFoundTitle" resname="folderNotFoundTitle">
 				<source>Folder not found.</source>