Commit 2b23357d authored by Georg Ringer's avatar Georg Ringer Committed by Daniel Goerz
Browse files

[TASK] Migrate LoginWarning mails to FluidEmail

Resolves: #90376
Releases: master
Change-Id: I0d7643b9bd0bd99545cd5e488a87cd381219cc42
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63250


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Tested-by: Susanne Moog's avatarSusanne Moog <look@susi.dev>
Tested-by: Daniel Goerz's avatarDaniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Reviewed-by: Susanne Moog's avatarSusanne Moog <look@susi.dev>
Reviewed-by: Daniel Goerz's avatarDaniel Goerz <daniel.goerz@posteo.de>
parent 1da2d177
<f:layout name="SystemEmail" />
<f:section name="Subject">TYPO3 Login Failure Warning (at {typo3.sitename})</f:section>
<f:section name="Title">TYPO3 Login Failure Warning at {typo3.sitename}</f:section>
<f:section name="Main">
<strong>There have been some attempts ({lines -> f:count()}) to login at the TYPO3 site "{typo3.sitename}" (<strong>{normalizedParams.remoteAddress}</strong>).</strong>
<br><br>
This is a dump of the failures:<br>
<f:for each="{lines}" as="line">
{f:format.date(date:line.row.tstamp, format:'{typo3.formats.date} {typo3.formats.time}')}: {line.text}<br>
</f:for>
</f:section>
<f:layout name="SystemEmail" />
<f:section name="Subject">TYPO3 Login Failure Warning (at {typo3.sitename})</f:section>
<f:section name="Title">TYPO3 Login Failure Warning at {typo3.sitename}</f:section>
<f:section name="Main">There have been some attempts ({lines -> f:count()}) to login at the TYPO3 site "{typo3.sitename}" ({normalizedParams.remoteAddress}).
This is a dump of the failures:
<f:for each="{lines}" as="line">{f:format.date(date:line.row.tstamp, format:'{typo3.formats.date} {typo3.formats.time}')}: {line.text -> f:format.raw()}
</f:for>
</f:section>
......@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Core\Authentication;
* The TYPO3 project - inspiring people to share!
*/
use Doctrine\DBAL\Driver\Statement;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Cache\CacheManager;
......@@ -27,6 +29,8 @@ use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Mail\FluidEmail;
use TYPO3\CMS\Core\Mail\Mailer;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\ResourceStorage;
use TYPO3\CMS\Core\SysLog\Action as SystemLogGenericAction;
......@@ -2333,91 +2337,101 @@ TCAdefaults.sys_note.email = ' . $this->user['email'];
*/
public function checkLogFailures($email, $secondsBack = 3600, $max = 3)
{
if ($email) {
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
if (!GeneralUtility::validEmail($email)) {
return;
}
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
// Get last flag set in the log for sending
$theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log');
$queryBuilder->select('tstamp')
->from('sys_log')
->where(
$queryBuilder->expr()->eq(
'type',
$queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
'action',
$queryBuilder->createNamedParameter(4, \PDO::PARAM_INT)
),
$queryBuilder->expr()->gt(
'tstamp',
$queryBuilder->createNamedParameter($theTimeBack, \PDO::PARAM_INT)
)
// Get last flag set in the log for sending
$theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log');
$queryBuilder->select('tstamp')
->from('sys_log')
->where(
$queryBuilder->expr()->eq(
'type',
$queryBuilder->createNamedParameter(SystemLogType::LOGIN, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
'action',
$queryBuilder->createNamedParameter(SystemLogLoginAction::SEND_FAILURE_WARNING_EMAIL, \PDO::PARAM_INT)
),
$queryBuilder->expr()->gt(
'tstamp',
$queryBuilder->createNamedParameter($theTimeBack, \PDO::PARAM_INT)
)
->orderBy('tstamp', 'DESC')
->setMaxResults(1);
if ($testRow = $queryBuilder->execute()->fetch(\PDO::FETCH_ASSOC)) {
$theTimeBack = $testRow['tstamp'];
}
)
->orderBy('tstamp', 'DESC')
->setMaxResults(1);
if ($testRow = $queryBuilder->execute()->fetch(\PDO::FETCH_ASSOC)) {
$theTimeBack = $testRow['tstamp'];
}
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log');
$result = $queryBuilder->select('*')
->from('sys_log')
->where(
$queryBuilder->expr()->eq(
'type',
$queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
'action',
$queryBuilder->createNamedParameter(3, \PDO::PARAM_INT)
),
$queryBuilder->expr()->neq(
'error',
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
),
$queryBuilder->expr()->gt(
'tstamp',
$queryBuilder->createNamedParameter($theTimeBack, \PDO::PARAM_INT)
)
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log');
$result = $queryBuilder->select('*')
->from('sys_log')
->where(
$queryBuilder->expr()->eq(
'type',
$queryBuilder->createNamedParameter(SystemLogType::LOGIN, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
'action',
$queryBuilder->createNamedParameter(SystemLogLoginAction::ATTEMPT, \PDO::PARAM_INT)
),
$queryBuilder->expr()->neq(
'error',
$queryBuilder->createNamedParameter(SystemLogErrorClassification::MESSAGE, \PDO::PARAM_INT)
),
$queryBuilder->expr()->gt(
'tstamp',
$queryBuilder->createNamedParameter($theTimeBack, \PDO::PARAM_INT)
)
->orderBy('tstamp')
->execute();
)
->orderBy('tstamp')
->execute();
$rowCount = $queryBuilder
->count('uid')
->execute()
->fetchColumn(0);
// Check for more than $max number of error failures with the last period.
if ($rowCount > $max) {
// OK, so there were more than the max allowed number of login failures - so we will send an email then.
$subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')';
$email_body = 'There have been some attempts (' . $rowCount . ') to login at the TYPO3
site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . GeneralUtility::getIndpEnv('HTTP_HOST') . ').
This is a dump of the failures:
';
while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
$theData = unserialize($row['log_data']);
$text = @sprintf($row['details'], (string)$theData[0], (string)$theData[1], (string)$theData[2]);
if ((int)$row['type'] === 255) {
$text = str_replace('###IP###', $row['IP'], $text);
}
$email_body .= date(
$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'],
$row['tstamp']
) . ': ' . $text;
$email_body .= LF;
}
$mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
$mail->setTo($email)->subject($subject)->text($email_body);
$mail->send();
// Logout written to log
$this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::SEND_FAILURE_WARNING_EMAIL, SystemLogErrorClassification::MESSAGE, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', [$rowCount, $secondsBack, $email]);
$rowCount = $queryBuilder
->count('uid')
->execute()
->fetchColumn(0);
// Check for more than $max number of error failures with the last period.
if ($rowCount > $max) {
// OK, so there were more than the max allowed number of login failures - so we will send an email then.
$this->sendLoginAttemptEmail($result, $email);
// Login failure attempt written to log
$this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::SEND_FAILURE_WARNING_EMAIL, SystemLogErrorClassification::MESSAGE, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', [$rowCount, $secondsBack, $email]);
}
}
/**
* Sends out an email if the number of attempts have exceeded a limit.
*
* @param Statement $result
* @param string $emailAddress
*/
protected function sendLoginAttemptEmail(Statement $result, string $emailAddress): void
{
$emailData = [];
while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
$theData = unserialize($row['log_data'], ['allowed_classes' => false]);
$text = @sprintf($row['details'], (string)$theData[0], (string)$theData[1], (string)$theData[2]);
if ((int)$row['type'] === SystemLogType::LOGIN) {
$text = str_replace('###IP###', $row['IP'], $text);
}
$emailData[] = [
'row' => $row,
'text' => $text
];
}
$email = GeneralUtility::makeInstance(FluidEmail::class)
->to($emailAddress)
->setTemplate('Security/LoginAttemptFailedWarning')
->assign('lines', $emailData);
if ($GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface) {
$email->setRequest($GLOBALS['TYPO3_REQUEST']);
}
GeneralUtility::makeInstance(Mailer::class)->send($email);
}
/**
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment