2 namespace TYPO3\CMS\Reports\Report\Status
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Backend\Utility\BackendUtility
;
18 use TYPO3\CMS\Core\Messaging\FlashMessage
;
19 use TYPO3\CMS\Core\Utility\GeneralUtility
;
20 use TYPO3\CMS\Install\Service\EnableFileService
;
21 use TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility
;
24 * Performs several checks about the system's health
26 * @author Ingo Renner <ingo@typo3.org>
28 class SecurityStatus
implements \TYPO3\CMS\Reports\StatusProviderInterface
{
31 * Determines the Install Tool's status, mainly concerning its protection.
33 * @return array List of statuses
35 public function getStatus() {
36 $this->executeAdminCommand();
38 'adminUserAccount' => $this->getAdminAccountStatus(),
39 'encryptionKeyEmpty' => $this->getEncryptionKeyStatus(),
40 'fileDenyPattern' => $this->getFileDenyPatternStatus(),
41 'htaccessUpload' => $this->getHtaccessUploadStatus(),
42 'installToolEnabled' => $this->getInstallToolProtectionStatus(),
43 'installToolPassword' => $this->getInstallToolPasswordStatus(),
44 'saltedpasswords' => $this->getSaltedPasswordsStatus()
50 * Checks whether a an BE user account named admin with default password exists.
52 * @return \TYPO3\CMS\Reports\Status An object representing whether a default admin account exists
54 protected function getAdminAccountStatus() {
55 $value = $GLOBALS['LANG']->getLL('status_ok');
57 $severity = \TYPO3\CMS\Reports\Status
::OK
;
58 $whereClause = 'username = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr('admin', 'be_users') .
59 BackendUtility
::deleteClause('be_users');
60 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid, username, password', 'be_users', $whereClause);
61 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
64 /** @var $saltingObject \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface */
65 $saltingObject = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory
::getSaltingInstance($row['password']);
66 if (is_object($saltingObject)) {
67 if ($saltingObject->checkPassword('password', $row['password'])) {
71 // Check against plain MD5
72 if ($row['password'] === '5f4dcc3b5aa765d61d8327deb882cf99') {
76 $value = $GLOBALS['LANG']->getLL('status_insecure');
77 $severity = \TYPO3\CMS\Reports\Status
::ERROR
;
78 $editUserAccountUrl = 'alt_doc.php?returnUrl=' .
79 rawurlencode(BackendUtility
::getModuleUrl('system_ReportsTxreportsm1')) . '&edit[be_users][' . $row['uid'] . ']=edit';
80 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.backend_admin'),
81 '<a href="' . htmlspecialchars($editUserAccountUrl) . '">', '</a>');
84 $GLOBALS['TYPO3_DB']->sql_free_result($res);
85 return GeneralUtility
::makeInstance(\TYPO3\CMS\Reports\Status
::class,
86 $GLOBALS['LANG']->getLL('status_adminUserAccount'), $value, $message, $severity);
90 * Checks whether the encryption key is empty.
92 * @return \TYPO3\CMS\Reports\Status An object representing whether the encryption key is empty or not
94 protected function getEncryptionKeyStatus() {
95 $value = $GLOBALS['LANG']->getLL('status_ok');
97 $severity = \TYPO3\CMS\Reports\Status
::OK
;
98 if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
99 $value = $GLOBALS['LANG']->getLL('status_insecure');
100 $severity = \TYPO3\CMS\Reports\Status
::ERROR
;
101 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
102 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_encryption'),
103 '<a href="' . $url . '">', '</a>');
105 return GeneralUtility
::makeInstance(\TYPO3\CMS\Reports\Status
::class,
106 $GLOBALS['LANG']->getLL('status_encryptionKey'), $value, $message, $severity);
110 * Checks if fileDenyPattern was changed which is dangerous on Apache
112 * @return \TYPO3\CMS\Reports\Status An object representing whether the file deny pattern has changed
114 protected function getFileDenyPatternStatus() {
115 $value = $GLOBALS['LANG']->getLL('status_ok');
117 $severity = \TYPO3\CMS\Reports\Status
::OK
;
118 $defaultParts = GeneralUtility
::trimExplode('|', FILE_DENY_PATTERN_DEFAULT
, TRUE);
119 $givenParts = GeneralUtility
::trimExplode('|', $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'], TRUE);
120 $result = array_intersect($defaultParts, $givenParts);
121 if ($defaultParts !== $result) {
122 $value = $GLOBALS['LANG']->getLL('status_insecure');
123 $severity = \TYPO3\CMS\Reports\Status
::ERROR
;
124 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
125 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_pattern_partsNotPresent'),
126 '<br /><pre>' . htmlspecialchars(FILE_DENY_PATTERN_DEFAULT
) . '</pre><br />');
128 return GeneralUtility
::makeInstance(\TYPO3\CMS\Reports\Status
::class,
129 $GLOBALS['LANG']->getLL('status_fileDenyPattern'), $value, $message, $severity);
133 * Checks if fileDenyPattern allows to upload .htaccess files which is
134 * dangerous on Apache.
136 * @return \TYPO3\CMS\Reports\Status An object representing whether it's possible to upload .htaccess files
138 protected function getHtaccessUploadStatus() {
139 $value = $GLOBALS['LANG']->getLL('status_ok');
141 $severity = \TYPO3\CMS\Reports\Status
::OK
;
142 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] != FILE_DENY_PATTERN_DEFAULT
143 && GeneralUtility
::verifyFilenameAgainstDenyPattern('.htaccess')) {
144 $value = $GLOBALS['LANG']->getLL('status_insecure');
145 $severity = \TYPO3\CMS\Reports\Status
::ERROR
;
146 $message = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_htaccess');
148 return GeneralUtility
::makeInstance(\TYPO3\CMS\Reports\Status
::class,
149 $GLOBALS['LANG']->getLL('status_htaccessUploadProtection'), $value, $message, $severity);
153 * Checks whether memcached is configured, if that's the case we assume it's also used.
155 * @return bool TRUE if memcached is used, FALSE otherwise.
157 protected function isMemcachedUsed() {
158 $memcachedUsed = FALSE;
159 $memcachedServers = $this->getConfiguredMemcachedServers();
160 if (count($memcachedServers)) {
161 $memcachedUsed = TRUE;
163 return $memcachedUsed;
167 * Executes commands like removing the Install Tool enable file.
171 protected function executeAdminCommand() {
172 $command = GeneralUtility
::_GET('adminCmd');
174 case 'remove_ENABLE_INSTALL_TOOL':
175 EnableFileService
::removeInstallToolEnableFile();
183 * Checks whether the Install Tool password is set to its default value.
185 * @return \TYPO3\CMS\Reports\Status An object representing the security of the install tool password
187 protected function getInstallToolPasswordStatus() {
188 $value = $GLOBALS['LANG']->getLL('status_ok');
190 $severity = \TYPO3\CMS\Reports\Status
::OK
;
191 $validPassword = TRUE;
192 $installToolPassword = $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'];
193 $saltFactory = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory
::getSaltingInstance($installToolPassword);
194 if (is_object($saltFactory)) {
195 $validPassword = !$saltFactory->checkPassword('joh316', $installToolPassword);
196 } elseif ($installToolPassword === md5('joh316')) {
197 $validPassword = FALSE;
199 if (!$validPassword) {
200 $value = $GLOBALS['LANG']->getLL('status_insecure');
201 $severity = \TYPO3\CMS\Reports\Status
::ERROR
;
202 $changeInstallToolPasswordUrl = BackendUtility
::getModuleUrl('system_InstallInstall');
203 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.installtool_default_password'),
204 '<a href="' . htmlspecialchars($changeInstallToolPasswordUrl) . '">', '</a>');
206 return GeneralUtility
::makeInstance(\TYPO3\CMS\Reports\Status
::class,
207 $GLOBALS['LANG']->getLL('status_installToolPassword'), $value, $message, $severity);
211 * Checks whether the Install Tool password is set to its default value.
213 * @return \TYPO3\CMS\Reports\Status An object representing the security of the saltedpassswords extension
215 protected function getSaltedPasswordsStatus() {
216 $value = $GLOBALS['LANG']->getLL('status_ok');
217 $severity = \TYPO3\CMS\Reports\Status
::OK
;
218 /** @var \TYPO3\CMS\Saltedpasswords\Utility\ExtensionManagerConfigurationUtility $configCheck */
219 $configCheck = GeneralUtility
::makeInstance(\TYPO3\CMS\Saltedpasswords\Utility\ExtensionManagerConfigurationUtility
::class);
220 $message = '<p>' . $GLOBALS['LANG']->getLL('status_saltedPasswords_infoText') . '</p>';
222 $resultCheck = $configCheck->checkConfigurationBackend(array(), new \TYPO3\CMS\Core\TypoScript\
ConfigurationForm());
223 switch ($resultCheck['errorType']) {
224 case FlashMessage
::INFO
:
225 $messageDetail .= $resultCheck['html'];
227 case FlashMessage
::WARNING
;
228 $severity = \TYPO3\CMS\Reports\Status
::WARNING
;
229 $messageDetail .= $resultCheck['html'];
231 case FlashMessage
::ERROR
:
232 $value = $GLOBALS['LANG']->getLL('status_insecure');
233 $severity = \TYPO3\CMS\Reports\Status
::ERROR
;
234 $messageDetail .= $resultCheck['html'];
238 $unsecureUserCount = SaltedPasswordsUtility
::getNumberOfBackendUsersWithInsecurePassword();
239 if ($unsecureUserCount > 0) {
240 $value = $GLOBALS['LANG']->getLL('status_insecure');
241 $severity = \TYPO3\CMS\Reports\Status
::ERROR
;
242 $messageDetail .= '<div class="panel panel-warning">' .
243 '<div class="panel-body">' .
244 $GLOBALS['LANG']->getLL('status_saltedPasswords_notAllPasswordsHashed') .
248 $message .= $messageDetail;
249 if (empty($messageDetail)) {
252 return GeneralUtility
::makeInstance(\TYPO3\CMS\Reports\Status
::class,
253 $GLOBALS['LANG']->getLL('status_saltedPasswords'), $value, $message, $severity);
257 * Checks for the existence of the ENABLE_INSTALL_TOOL file.
259 * @return \TYPO3\CMS\Reports\Status An object representing whether ENABLE_INSTALL_TOOL exists
261 protected function getInstallToolProtectionStatus() {
262 $enableInstallToolFile = PATH_site
. 'typo3conf/ENABLE_INSTALL_TOOL';
263 $value = $GLOBALS['LANG']->getLL('status_disabled');
265 $severity = \TYPO3\CMS\Reports\Status
::OK
;
266 if (EnableFileService
::installToolEnableFileExists()) {
267 if (EnableFileService
::isInstallToolEnableFilePermanent()) {
268 $severity = \TYPO3\CMS\Reports\Status
::WARNING
;
269 $disableInstallToolUrl = GeneralUtility
::getIndpEnv('TYPO3_REQUEST_URL') . '&adminCmd=remove_ENABLE_INSTALL_TOOL';
270 $value = $GLOBALS['LANG']->getLL('status_enabledPermanently');
271 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_enabled'),
272 '<span style="white-space: nowrap;">' . $enableInstallToolFile . '</span>');
273 $message .= ' <a href="' . $disableInstallToolUrl . '">' .
274 $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_enabled_cmd') . '</a>';
276 if (EnableFileService
::installToolEnableFileLifetimeExpired()) {
277 EnableFileService
::removeInstallToolEnableFile();
279 $severity = \TYPO3\CMS\Reports\Status
::NOTICE
;
280 $disableInstallToolUrl = GeneralUtility
::getIndpEnv('TYPO3_REQUEST_URL') . '&adminCmd=remove_ENABLE_INSTALL_TOOL';
281 $value = $GLOBALS['LANG']->getLL('status_enabledTemporarily');
282 $message = sprintf($GLOBALS['LANG']->getLL('status_installEnabledTemporarily'),
283 '<span style="white-space: nowrap;">' . $enableInstallToolFile . '</span>', floor($enableInstallToolFileTtl / 60));
284 $message .= ' <a href="' . $disableInstallToolUrl . '">' .
285 $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_enabled_cmd') . '</a>';
289 return GeneralUtility
::makeInstance(\TYPO3\CMS\Reports\Status
::class,
290 $GLOBALS['LANG']->getLL('status_installTool'), $value, $message, $severity);