Skip to content

Commit b39e129

Browse files
committed
[SECURITY] Add hmac validation to file downloads in be module
It was possible to download an arbitrary file from a TYPO3 installation using the download link in the powermail module. This commits adds a hmac to the link and validates it before starting the download.
1 parent 99b2364 commit b39e129

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

Classes/Controller/ModuleController.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,10 @@ protected function checkAdminPermissions(): ?ResponseInterface
353353
public function downloadFile(ServerRequestInterface $request): ?ResponseInterface
354354
{
355355
$queryParams = $request->getQueryParams();
356-
if (array_key_exists('file', $queryParams)) {
356+
if (array_key_exists('file', $queryParams) && array_key_exists('hmac', $queryParams)) {
357357
$fileName = basename($queryParams['file']);
358358
$absoluteFileName = GeneralUtility::getFileAbsFileName($queryParams['file']);
359-
if (is_file($absoluteFileName)) {
359+
if (is_file($absoluteFileName) && $this->isValidHmac($absoluteFileName, $queryParams['hmac'])) {
360360
(mime_content_type($absoluteFileName) === false) ? $mimeType = '' : $mimeType = mime_content_type($absoluteFileName);
361361
return $this->responseFactory->createResponse()
362362
->withHeader('Content-Type', $mimeType)
@@ -367,4 +367,10 @@ public function downloadFile(ServerRequestInterface $request): ?ResponseInterfac
367367

368368
return new ForwardResponse('list');
369369
}
370+
371+
protected function isValidHmac(string $fileName, string $hmacFromQuery): bool
372+
{
373+
$hmacGenerated = BasicFileUtility::getHmacForFile($fileName);
374+
return hash_equals($hmacGenerated, $hmacFromQuery);
375+
}
370376
}

Classes/Utility/BasicFileUtility.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,9 @@ public static function getRelativeFolder(string $path): string
8181

8282
return $path;
8383
}
84+
85+
public static function getHmacForFile(string $file): string
86+
{
87+
return GeneralUtility::hmac($file, '_powermail');
88+
}
8489
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace In2code\Powermail\ViewHelpers\Misc;
6+
7+
use In2code\Powermail\Utility\BasicFileUtility;
8+
use TYPO3\CMS\Core\Utility\GeneralUtility;
9+
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
10+
11+
/**
12+
* Get Upload Path ViewHelper
13+
*/
14+
class GetHmacForFileViewHelper extends AbstractTagBasedViewHelper
15+
{
16+
protected string $uploadPathFallback = 'uploads/tx_powermail/';
17+
18+
public function initializeArguments(): void
19+
{
20+
parent::initializeArguments();
21+
$this->registerArgument('fileName', 'string', 'Filename like "picture.jpg"', true);
22+
$this->registerArgument('path', 'string', 'Path like "fileadmin/powermail/uploads/"', true);
23+
}
24+
25+
public function render(): string
26+
{
27+
$fileName = $this->arguments['fileName'] ?? '';
28+
$path = $this->arguments['path'] ?? $this->uploadPathFallback;
29+
30+
$absFileName = GeneralUtility::getFileAbsFileName($path . $fileName);
31+
32+
return BasicFileUtility::getHmacForFile($absFileName);
33+
}
34+
}

Resources/Private/Partials/Module/List.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,7 @@
221221
</f:else>
222222
</f:if>
223223
</a>
224-
225-
<a href="{be:moduleLink(route:'powermail_downloadfile', arguments:'{file: \'{settings.uploadPath}{subValue}\'}')}" download>
224+
<a href="{be:moduleLink(route:'powermail_downloadfile', arguments:'{file: \'{settings.uploadPath}{subValue}\', hmac: \'{vh:misc.getHmacForFile(fileName:subValue, path:settings.uploadPath)}\'}')}" download>
226225
Download
227226
</a>
228227
</div>

0 commit comments

Comments
 (0)