Skip to content

Commit bfe7c35

Browse files
committed
[SECURITY] Check file permissions before showing meta data
Several HTTP routes of the backend API could be used to retrieve file meta data without checking user permissions properly. Resolves: #109842 Releases: main, 14.3, 13.4 Change-Id: I9189bdbcbc41977c597a259b51485f0e725fbdbf Security-Bulletin: TYPO3-CORE-SA-2026-015. Security-References: CVE-2026-47352 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/94422 Reviewed-by: Oliver Hader <oliver.hader@typo3.org> Tested-by: Oliver Hader <oliver.hader@typo3.org>
1 parent 932fbb9 commit bfe7c35

4 files changed

Lines changed: 26 additions & 5 deletions

File tree

typo3/sysext/backend/Classes/Controller/File/ImageProcessController.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public function process(ServerRequestInterface $request): ResponseInterface
4242
$processedFileId = (int)($request->getQueryParams()['id'] ?? 0);
4343
try {
4444
$processedFile = $this->imageProcessingService->process($processedFileId);
45+
if (!$processedFile->getOriginalFile()->checkActionPermission('read')) {
46+
return new HtmlResponse('', 403);
47+
}
4548

4649
return new RedirectResponse(
4750
GeneralUtility::locationHeaderUrl($processedFile->getPublicUrl() ?? '', $request)

typo3/sysext/backend/Classes/Controller/LinkController.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use TYPO3\CMS\Core\Messaging\FlashMessage;
2727
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
2828
use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
29+
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
2930
use TYPO3\CMS\Core\Resource\File;
3031
use TYPO3\CMS\Core\Resource\Folder;
3132
use TYPO3\CMS\Core\Resource\ResourceFactory;
@@ -47,34 +48,45 @@ public function resourceAction(ServerRequestInterface $request): ResponseInterfa
4748
$identifier = $request->getParsedBody()['identifier'] ?? null;
4849
$resource = null;
4950

50-
if ($identifier) {
51-
$resource = $this->resourceFactory->retrieveFileOrFolderObject($identifier);
52-
}
53-
5451
try {
52+
if ($identifier) {
53+
$resource = $this->resourceFactory->retrieveFileOrFolderObject($identifier);
54+
}
5555
if (!$resource instanceof File && !$resource instanceof Folder) {
5656
throw new \InvalidArgumentException('Resource must be a file or a folder', 1679039649);
5757
}
5858
if ($resource->getStorage()->isFallbackStorage()) {
5959
throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1679039650);
6060
}
6161
if ($resource instanceof File) {
62+
if (!$resource->checkActionPermission('read')) {
63+
throw new InsufficientFileAccessPermissionsException('You are not allowed to access this file', 1779001351);
64+
}
6265
$parameters = [
6366
'type' => LinkService::TYPE_FILE,
6467
'file' => $resource,
6568
];
6669
}
6770
if ($resource instanceof Folder) {
71+
// Note: No explicit `$resource->checkActionPermission('read')` check here, as that would
72+
// be a no-op since `ResourceStorage::getFolder()` calls `assureFolderReadPermission()`
73+
// and throws `InsufficientFolderAccessPermissionsException`
6874
$parameters = [
6975
'type' => LinkService::TYPE_FOLDER,
7076
'folder' => $resource,
7177
];
7278
}
7379
$link = $this->linkService->asString($parameters);
80+
} catch (InsufficientFileAccessPermissionsException|InsufficientFolderAccessPermissionsException $exception) {
81+
$message = match ($exception->getCode()) {
82+
1679039650 => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.error.message.resourceOutsideOfStorages'),
83+
default => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.error.message.resourceNoPermissionRead'),
84+
};
85+
86+
return new JsonResponse($this->getResponseData(false, $message));
7487
} catch (\Exception $exception) {
7588
$message = match ($exception->getCode()) {
7689
1679039649 => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.error.message.resourceNotFileOrFolder'),
77-
1679039650 => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.error.message.resourceOutsideOfStorages'),
7890
default => $exception->getMessage(),
7991
};
8092

typo3/sysext/backend/Classes/Controller/Resource/ResourceController.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public function gatherInformationAction(ServerRequestInterface $request): Respon
6565
if ($resource === null) {
6666
return new JsonResponse(null, 404);
6767
}
68+
if (!$resource->checkActionPermission('read')) {
69+
return new JsonResponse(null, 403);
70+
}
6871

6972
return new JsonResponse($this->getResourceResponseData($resource));
7073
}

typo3/sysext/backend/Resources/Private/Language/locallang_resource.xlf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
<trans-unit id="ajax.error.message.resourceOutsideOfStorages">
2222
<source>Access to files outside your configured storages is not permitted.</source>
2323
</trans-unit>
24+
<trans-unit id="ajax.error.message.resourceNoPermissionRead">
25+
<source>Reading this resource is not permitted.</source>
26+
</trans-unit>
2427
<trans-unit id="ajax.error.message.resourceNoPermissionRename">
2528
<source>Renaming this resource is not permitted.</source>
2629
</trans-unit>

0 commit comments

Comments
 (0)