[CLEANUP] Code cleanup in ext:reports
[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\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Database\DatabaseConnection;
19 use TYPO3\CMS\Core\Messaging\FlashMessage;
20 use TYPO3\CMS\Core\TypoScript\ConfigurationForm;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Lang\LanguageService;
23 use TYPO3\CMS\Reports\Status;
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 \TYPO3\CMS\Reports\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 = array(
41 'trustedHostsPattern' => $this->getTrustedHostsPatternStatus(),
42 'adminUserAccount' => $this->getAdminAccountStatus(),
43 'encryptionKeyEmpty' => $this->getEncryptionKeyStatus(),
44 'fileDenyPattern' => $this->getFileDenyPatternStatus(),
45 'htaccessUpload' => $this->getHtaccessUploadStatus(),
46 'saltedpasswords' => $this->getSaltedPasswordsStatus()
47 );
48 return $statuses;
49 }
50
51 /**
52 * Checks if the trusted hosts pattern check is disabled.
53 *
54 * @return \TYPO3\CMS\Reports\Status An object representing whether the check is disabled
55 */
56 protected function getTrustedHostsPatternStatus()
57 {
58 $value = $this->getLanguageService()->getLL('status_ok');
59 $message = '';
60 $severity = Status::OK;
61 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
62 $value = $this->getLanguageService()->getLL('status_insecure');
63 $severity = Status::ERROR;
64 $message = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_trustedhosts');
65 }
66 return GeneralUtility::makeInstance(Status::class, $this->getLanguageService()->getLL('status_trustedHostsPattern'), $value, $message, $severity);
67 }
68
69 /**
70 * Checks whether a BE user account named admin with default password exists.
71 *
72 * @return \TYPO3\CMS\Reports\Status An object representing whether a default admin account exists
73 */
74 protected function getAdminAccountStatus()
75 {
76 $value = $this->getLanguageService()->getLL('status_ok');
77 $message = '';
78 $severity = Status::OK;
79 $whereClause = 'username = ' . $this->getDatabaseConnection()->fullQuoteStr('admin', 'be_users') .
80 BackendUtility::deleteClause('be_users');
81 $res = $this->getDatabaseConnection()->exec_SELECTquery('uid, username, password', 'be_users', $whereClause);
82 $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
83 if (!empty($row)) {
84 $secure = true;
85 /** @var \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface $saltingObject */
86 $saltingObject = SaltFactory::getSaltingInstance($row['password']);
87 if (is_object($saltingObject)) {
88 if ($saltingObject->checkPassword('password', $row['password'])) {
89 $secure = false;
90 }
91 }
92 // Check against plain MD5
93 if ($row['password'] === '5f4dcc3b5aa765d61d8327deb882cf99') {
94 $secure = false;
95 }
96 if (!$secure) {
97 $value = $this->getLanguageService()->getLL('status_insecure');
98 $severity = Status::ERROR;
99 $editUserAccountUrl = BackendUtility::getModuleUrl(
100 'record_edit',
101 array(
102 'edit[be_users][' . $row['uid'] . ']' => 'edit',
103 'returnUrl' => BackendUtility::getModuleUrl('system_ReportsTxreportsm1')
104 )
105 );
106 $message = sprintf(
107 $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.backend_admin'),
108 '<a href="' . htmlspecialchars($editUserAccountUrl) . '">',
109 '</a>'
110 );
111 }
112 }
113 $this->getDatabaseConnection()->sql_free_result($res);
114 return GeneralUtility::makeInstance(Status::class, $this->getLanguageService()->getLL('status_adminUserAccount'), $value, $message, $severity);
115 }
116
117 /**
118 * Checks whether the encryption key is empty.
119 *
120 * @return \TYPO3\CMS\Reports\Status An object representing whether the encryption key is empty or not
121 */
122 protected function getEncryptionKeyStatus()
123 {
124 $value = $this->getLanguageService()->getLL('status_ok');
125 $message = '';
126 $severity = Status::OK;
127 if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
128 $value = $this->getLanguageService()->getLL('status_insecure');
129 $severity = Status::ERROR;
130 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
131 $message = sprintf(
132 $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_encryption'),
133 '<a href="' . $url . '">',
134 '</a>'
135 );
136 }
137 return GeneralUtility::makeInstance(Status::class, $this->getLanguageService()->getLL('status_encryptionKey'), $value, $message, $severity);
138 }
139
140 /**
141 * Checks if fileDenyPattern was changed which is dangerous on Apache
142 *
143 * @return \TYPO3\CMS\Reports\Status An object representing whether the file deny pattern has changed
144 */
145 protected function getFileDenyPatternStatus()
146 {
147 $value = $this->getLanguageService()->getLL('status_ok');
148 $message = '';
149 $severity = Status::OK;
150 $defaultParts = GeneralUtility::trimExplode('|', FILE_DENY_PATTERN_DEFAULT, true);
151 $givenParts = GeneralUtility::trimExplode('|', $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'], true);
152 $result = array_intersect($defaultParts, $givenParts);
153 if ($defaultParts !== $result) {
154 $value = $this->getLanguageService()->getLL('status_insecure');
155 $severity = Status::ERROR;
156 $message = sprintf(
157 $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_pattern_partsNotPresent'),
158 '<br /><pre>' . htmlspecialchars(FILE_DENY_PATTERN_DEFAULT) . '</pre><br />'
159 );
160 }
161 return GeneralUtility::makeInstance(Status::class, $this->getLanguageService()->getLL('status_fileDenyPattern'), $value, $message, $severity);
162 }
163
164 /**
165 * Checks if fileDenyPattern allows to upload .htaccess files which is
166 * dangerous on Apache.
167 *
168 * @return \TYPO3\CMS\Reports\Status An object representing whether it's possible to upload .htaccess files
169 */
170 protected function getHtaccessUploadStatus()
171 {
172 $value = $this->getLanguageService()->getLL('status_ok');
173 $message = '';
174 $severity = Status::OK;
175 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] != FILE_DENY_PATTERN_DEFAULT
176 && GeneralUtility::verifyFilenameAgainstDenyPattern('.htaccess')) {
177 $value = $this->getLanguageService()->getLL('status_insecure');
178 $severity = Status::ERROR;
179 $message = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_htaccess');
180 }
181 return GeneralUtility::makeInstance(Status::class, $this->getLanguageService()->getLL('status_htaccessUploadProtection'), $value, $message, $severity);
182 }
183
184 /**
185 * Checks whether salted Passwords are configured or not.
186 *
187 * @return \TYPO3\CMS\Reports\Status An object representing the security of the saltedpassswords extension
188 */
189 protected function getSaltedPasswordsStatus()
190 {
191 $value = $this->getLanguageService()->getLL('status_ok');
192 $severity = Status::OK;
193 /** @var ExtensionManagerConfigurationUtility $configCheck */
194 $configCheck = GeneralUtility::makeInstance(ExtensionManagerConfigurationUtility::class);
195 $message = '<p>' . $this->getLanguageService()->getLL('status_saltedPasswords_infoText') . '</p>';
196 $messageDetail = '';
197 $resultCheck = $configCheck->checkConfigurationBackend(array(), new ConfigurationForm());
198 switch ($resultCheck['errorType']) {
199 case FlashMessage::INFO:
200 $messageDetail .= $resultCheck['html'];
201 break;
202 case FlashMessage::WARNING:
203 $severity = Status::WARNING;
204 $messageDetail .= $resultCheck['html'];
205 break;
206 case FlashMessage::ERROR:
207 $value = $this->getLanguageService()->getLL('status_insecure');
208 $severity = Status::ERROR;
209 $messageDetail .= $resultCheck['html'];
210 break;
211 default:
212 }
213 $unsecureUserCount = SaltedPasswordsUtility::getNumberOfBackendUsersWithInsecurePassword();
214 if ($unsecureUserCount > 0) {
215 $value = $this->getLanguageService()->getLL('status_insecure');
216 $severity = Status::ERROR;
217 $messageDetail .= '<div class="panel panel-warning">' .
218 '<div class="panel-body">' .
219 $this->getLanguageService()->getLL('status_saltedPasswords_notAllPasswordsHashed') .
220 '</div>' .
221 '</div>';
222 }
223 $message .= $messageDetail;
224 if (empty($messageDetail)) {
225 $message = '';
226 }
227 return GeneralUtility::makeInstance(Status::class, $this->getLanguageService()->getLL('status_saltedPasswords'), $value, $message, $severity);
228 }
229
230 /**
231 * @return LanguageService
232 */
233 protected function getLanguageService()
234 {
235 return $GLOBALS['LANG'];
236 }
237
238 /**
239 * @return DatabaseConnection
240 */
241 protected function getDatabaseConnection()
242 {
243 return $GLOBALS['TYPO3_DB'];
244 }
245 }