[TASK] Use BE Routing / PSR-7 instead of BackendUtility::getModuleUrl
[Packages/TYPO3.CMS.git] / typo3 / sysext / reports / Classes / Report / Status / SecurityStatus.php
1 <?php
2 namespace TYPO3\CMS\Reports\Report\Status;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Database\ConnectionPool;
18 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
19 use TYPO3\CMS\Core\Localization\LanguageService;
20 use TYPO3\CMS\Core\Messaging\FlashMessage;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Reports\Status as ReportStatus;
23 use TYPO3\CMS\Reports\StatusProviderInterface;
24 use TYPO3\CMS\Saltedpasswords\Salt\SaltFactory;
25 use TYPO3\CMS\Saltedpasswords\Utility\ExtensionManagerConfigurationUtility;
26 use TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility;
27
28 /**
29 * Performs several checks about the system's health
30 */
31 class SecurityStatus implements StatusProviderInterface
32 {
33 /**
34 * Determines the security of this TYPO3 installation
35 *
36 * @return \TYPO3\CMS\Reports\Status[] List of statuses
37 */
38 public function getStatus()
39 {
40 $statuses = [
41 'trustedHostsPattern' => $this->getTrustedHostsPatternStatus(),
42 'adminUserAccount' => $this->getAdminAccountStatus(),
43 'fileDenyPattern' => $this->getFileDenyPatternStatus(),
44 'htaccessUpload' => $this->getHtaccessUploadStatus(),
45 'saltedpasswords' => $this->getSaltedPasswordsStatus(),
46 ];
47 return $statuses;
48 }
49
50 /**
51 * Checks if the trusted hosts pattern check is disabled.
52 *
53 * @return \TYPO3\CMS\Reports\Status An object representing whether the check is disabled
54 */
55 protected function getTrustedHostsPatternStatus()
56 {
57 $value = $this->getLanguageService()->getLL('status_ok');
58 $message = '';
59 $severity = ReportStatus::OK;
60 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
61 $value = $this->getLanguageService()->getLL('status_insecure');
62 $severity = ReportStatus::ERROR;
63 $message = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.install_trustedhosts');
64 }
65 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_trustedHostsPattern'), $value, $message, $severity);
66 }
67
68 /**
69 * Checks whether a BE user account named admin with default password exists.
70 *
71 * @return \TYPO3\CMS\Reports\Status An object representing whether a default admin account exists
72 */
73 protected function getAdminAccountStatus()
74 {
75 $value = $this->getLanguageService()->getLL('status_ok');
76 $message = '';
77 $severity = ReportStatus::OK;
78
79 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
80 $queryBuilder->getRestrictions()
81 ->removeAll()
82 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
83
84 $row = $queryBuilder
85 ->select('uid', 'username', 'password')
86 ->from('be_users')
87 ->where(
88 $queryBuilder->expr()->eq(
89 'username',
90 $queryBuilder->createNamedParameter('admin', \PDO::PARAM_STR)
91 )
92 )
93 ->execute()
94 ->fetch();
95
96 if (!empty($row)) {
97 $secure = true;
98 /** @var \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface $saltingObject */
99 $saltingObject = SaltFactory::getSaltingInstance($row['password']);
100 if (is_object($saltingObject)) {
101 if ($saltingObject->checkPassword('password', $row['password'])) {
102 $secure = false;
103 }
104 }
105 // Check against plain MD5
106 if ($row['password'] === '5f4dcc3b5aa765d61d8327deb882cf99') {
107 $secure = false;
108 }
109 if (!$secure) {
110 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
111 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
112 $value = $this->getLanguageService()->getLL('status_insecure');
113 $severity = ReportStatus::ERROR;
114 $editUserAccountUrl = (string)$uriBuilder->buildUriFromRoute(
115 'record_edit',
116 [
117 'edit[be_users][' . $row['uid'] . ']' => 'edit',
118 'returnUrl' => (string)$uriBuilder->buildUriFromRoute('system_ReportsTxreportsm1')
119 ]
120 );
121 $message = sprintf(
122 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.backend_admin'),
123 '<a href="' . htmlspecialchars($editUserAccountUrl) . '">',
124 '</a>'
125 );
126 }
127 }
128 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_adminUserAccount'), $value, $message, $severity);
129 }
130
131 /**
132 * Checks if fileDenyPattern was changed which is dangerous on Apache
133 *
134 * @return \TYPO3\CMS\Reports\Status An object representing whether the file deny pattern has changed
135 */
136 protected function getFileDenyPatternStatus()
137 {
138 $value = $this->getLanguageService()->getLL('status_ok');
139 $message = '';
140 $severity = ReportStatus::OK;
141 $defaultParts = GeneralUtility::trimExplode('|', FILE_DENY_PATTERN_DEFAULT, true);
142 $givenParts = GeneralUtility::trimExplode('|', $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'], true);
143 $result = array_intersect($defaultParts, $givenParts);
144 if ($defaultParts !== $result) {
145 $value = $this->getLanguageService()->getLL('status_insecure');
146 $severity = ReportStatus::ERROR;
147 $message = sprintf(
148 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_deny_pattern_partsNotPresent'),
149 '<br /><pre>' . htmlspecialchars(FILE_DENY_PATTERN_DEFAULT) . '</pre><br />'
150 );
151 }
152 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_fileDenyPattern'), $value, $message, $severity);
153 }
154
155 /**
156 * Checks if fileDenyPattern allows to upload .htaccess files which is
157 * dangerous on Apache.
158 *
159 * @return \TYPO3\CMS\Reports\Status An object representing whether it's possible to upload .htaccess files
160 */
161 protected function getHtaccessUploadStatus()
162 {
163 $value = $this->getLanguageService()->getLL('status_ok');
164 $message = '';
165 $severity = ReportStatus::OK;
166 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] != FILE_DENY_PATTERN_DEFAULT
167 && GeneralUtility::verifyFilenameAgainstDenyPattern('.htaccess')) {
168 $value = $this->getLanguageService()->getLL('status_insecure');
169 $severity = ReportStatus::ERROR;
170 $message = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.file_deny_htaccess');
171 }
172 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_htaccessUploadProtection'), $value, $message, $severity);
173 }
174
175 /**
176 * Checks whether salted Passwords are configured or not.
177 *
178 * @return \TYPO3\CMS\Reports\Status An object representing the security of the saltedpassswords extension
179 */
180 protected function getSaltedPasswordsStatus()
181 {
182 $value = $this->getLanguageService()->getLL('status_ok');
183 $severity = ReportStatus::OK;
184 /** @var ExtensionManagerConfigurationUtility $configCheck */
185 $configCheck = GeneralUtility::makeInstance(ExtensionManagerConfigurationUtility::class);
186 $message = '<p>' . $this->getLanguageService()->getLL('status_saltedPasswords_infoText') . '</p>';
187 $messageDetail = '';
188 $resultCheck = $configCheck->checkConfigurationBackend([]);
189 switch ($resultCheck['errorType']) {
190 case FlashMessage::INFO:
191 $messageDetail .= $resultCheck['html'];
192 break;
193 case FlashMessage::WARNING:
194 $severity = ReportStatus::WARNING;
195 $messageDetail .= $resultCheck['html'];
196 break;
197 case FlashMessage::ERROR:
198 $value = $this->getLanguageService()->getLL('status_insecure');
199 $severity = ReportStatus::ERROR;
200 $messageDetail .= $resultCheck['html'];
201 break;
202 default:
203 }
204 $unsecureUserCount = SaltedPasswordsUtility::getNumberOfBackendUsersWithInsecurePassword();
205 if ($unsecureUserCount > 0) {
206 $value = $this->getLanguageService()->getLL('status_insecure');
207 $severity = ReportStatus::ERROR;
208 $messageDetail .= '<div class="panel panel-warning">' .
209 '<div class="panel-body">' .
210 $this->getLanguageService()->getLL('status_saltedPasswords_notAllPasswordsHashed') .
211 '</div>' .
212 '</div>';
213 }
214 $message .= $messageDetail;
215 if (empty($messageDetail)) {
216 $message = '';
217 }
218 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_saltedPasswords'), $value, $message, $severity);
219 }
220
221 /**
222 * @return LanguageService
223 */
224 protected function getLanguageService()
225 {
226 return $GLOBALS['LANG'];
227 }
228 }