Skip to content

Commit 55f1e66

Browse files
committed
[FEATURE] Use CSRF like tokens
The use of the token is intended to prevent CSRF attacks. As a positive side effect, it also prevents (accidental) repeated sending. This occurs on iPhones, for example, when the browser is reopened and the success page was still open in a tab) See: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Authentication/CSRFlikeRequestTokenHandling.html Related: #286 Related: #1195
1 parent ec096b4 commit 55f1e66

File tree

4 files changed

+55
-0
lines changed

4 files changed

+55
-0
lines changed

Classes/Controller/FormController.php

+44
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@
3737
use Throwable;
3838
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException;
3939
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException;
40+
use TYPO3\CMS\Core\Context\Context;
41+
use TYPO3\CMS\Core\Context\SecurityAspect;
4042
use TYPO3\CMS\Core\Http\PropagateResponseException;
43+
use TYPO3\CMS\Core\Security\RequestToken;
4144
use TYPO3\CMS\Core\Utility\ArrayUtility;
4245
use TYPO3\CMS\Core\Utility\GeneralUtility;
4346
use TYPO3\CMS\Extbase\Annotation as ExtbaseAnnotation;
@@ -82,12 +85,14 @@ public function formAction(): ResponseInterface
8285
);
8386
$form = $event->getForm();
8487
SessionUtility::saveFormStartInSession($this->settings, $form);
88+
$requestToken = RequestToken::create('powermail/create');
8589
$this->view->assignMultiple(
8690
[
8791
'form' => $form,
8892
'ttContentData' => $this->contentObject->data,
8993
'messageClass' => $this->messageClass,
9094
'action' => ($this->settings['main']['confirmation'] ? 'checkConfirmation' : 'checkCreate'),
95+
'requestToken' => $requestToken,
9196
]
9297
);
9398

@@ -262,6 +267,35 @@ public function createAction(Mail $mail, string $hash = ''): ResponseInterface
262267
if ($mail->getUid() !== null && !HashUtility::isHashValid($hash, $mail)) {
263268
return (new ForwardResponse('form'))->withoutArguments();
264269
}
270+
$context = GeneralUtility::makeInstance(Context::class);
271+
$securityAspect = SecurityAspect::provideIn($context);
272+
$requestToken = $securityAspect->getReceivedRequestToken();
273+
274+
if ($requestToken === null) {
275+
$this->addFlashMessage(
276+
LocalizationUtility::translate('error_requesttoken_missing'),
277+
\TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR
278+
);
279+
$this->messageClass = 'error';
280+
return (new ForwardResponse('form'))->withArguments(['messageClass' => $this->messageClass]);
281+
}
282+
if ($requestToken === false) {
283+
$this->addFlashMessage(
284+
LocalizationUtility::translate('error_requesttoken_not_verified'),
285+
\TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR
286+
);
287+
$this->messageClass = 'error';
288+
return (new ForwardResponse('form'))->withArguments(['messageClass' => $this->messageClass]);
289+
}
290+
if ($requestToken->scope !== 'powermail/create') {
291+
$this->addFlashMessage(
292+
LocalizationUtility::translate('error_requesttoken_wrong_scope'),
293+
\TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR
294+
);
295+
$this->messageClass = 'error';
296+
return (new ForwardResponse('form'))->withArguments(['messageClass' => $this->messageClass]);
297+
}
298+
265299
$event = GeneralUtility::makeInstance(FormControllerCreateActionBeforeRenderViewEvent::class, $mail, $hash, $this);
266300
$this->eventDispatcher->dispatch($event);
267301
$mail = $event->getMail();
@@ -322,6 +356,14 @@ public function createAction(Mail $mail, string $hash = ''): ResponseInterface
322356
$this->contentObject
323357
);
324358

359+
// The middleware takes care to remove the cookie in case no other
360+
// nonce value shall be emitted during the current HTTP request
361+
if ($requestToken->getSigningSecretIdentifier() !== null) {
362+
$securityAspect->getSigningSecretResolver()->revokeIdentifier(
363+
$requestToken->getSigningSecretIdentifier(),
364+
);
365+
}
366+
325367
return $this->htmlResponse();
326368
}
327369

@@ -372,6 +414,7 @@ protected function sendMailPreflight(Mail $mail, string $hash = ''): void
372414
*/
373415
protected function prepareOutput(Mail $mail): void
374416
{
417+
$requestToken = RequestToken::create('powermail/create');
375418
$this->view->assignMultiple(
376419
[
377420
'variablesWithMarkers' => $this->mailRepository->getVariablesWithMarkersFromMail($mail, true),
@@ -382,6 +425,7 @@ protected function prepareOutput(Mail $mail): void
382425
'uploadService' => $this->uploadService,
383426
'powermail_rte' => $this->settings['thx']['body'],
384427
'powermail_all' => TemplateUtility::powermailAll($mail, 'web', $this->settings, $this->actionMethodName),
428+
'requestToken' => $requestToken,
385429
]
386430
);
387431
$this->view->assignMultiple($this->mailRepository->getVariablesWithMarkersFromMail($mail, true));

Resources/Private/Language/locallang.xlf

+9
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@
154154
<trans-unit id="error_mail_not_deleted" resname="error_mail_not_deleted">
155155
<source>Mail could not be deleted. Wrong link or mail was probably already removed from the system.</source>
156156
</trans-unit>
157+
<trans-unit id="error_requesttoken_missing" resname="error_requesttoken_missing">
158+
<source>No request token was provided in the request.</source>
159+
</trans-unit>
160+
<trans-unit id="error_requesttoken_not_verified" resname="error_requesttoken_not_verified">
161+
<source>The given request token could not be verified with the nonce.</source>
162+
</trans-unit>
163+
<trans-unit id="error_requesttoken_wrong_scope" resname="error_requesttoken_wrong_scope">
164+
<source>The given request token seems to be for a different scope.</source>
165+
</trans-unit>
157166
<trans-unit id="validationerror_mandatory" resname="validationerror_mandatory">
158167
<source>This field must be filled!</source>
159168
</trans-unit>

Resources/Private/Templates/Form/Confirmation.html

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ <h1><f:translate key="confirmation_message" /></h1>
4141
</f:comment>
4242
<f:form
4343
action="checkCreate"
44+
requestToken="{requestToken}"
4445
section="c{ttContentData.uid}"
4546
name="field"
4647
enctype="multipart/form-data"

Resources/Private/Templates/Form/Form.html

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<div class="container-fluid">
1818
<f:form
1919
action="{action}"
20+
requestToken="{requestToken}"
2021
section="c{ttContentData.uid}"
2122
name="field"
2223
enctype="multipart/form-data"

0 commit comments

Comments
 (0)