1acfaa39da402595383533b5709008ebcdbd6cb3
[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\Messaging\FlashMessage;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility;
21
22 /**
23 * Performs several checks about the system's health
24 */
25 class SecurityStatus implements \TYPO3\CMS\Reports\StatusProviderInterface {
26
27 /**
28 * Determines the security of this TYPO3 installation
29 *
30 * @return \TYPO3\CMS\Reports\Status[] List of statuses
31 */
32 public function getStatus() {
33 $statuses = array(
34 'adminUserAccount' => $this->getAdminAccountStatus(),
35 'encryptionKeyEmpty' => $this->getEncryptionKeyStatus(),
36 'fileDenyPattern' => $this->getFileDenyPatternStatus(),
37 'htaccessUpload' => $this->getHtaccessUploadStatus(),
38 'saltedpasswords' => $this->getSaltedPasswordsStatus()
39 );
40 return $statuses;
41 }
42
43 /**
44 * Checks whether a an BE user account named admin with default password exists.
45 *
46 * @return \TYPO3\CMS\Reports\Status An object representing whether a default admin account exists
47 */
48 protected function getAdminAccountStatus() {
49 $value = $GLOBALS['LANG']->getLL('status_ok');
50 $message = '';
51 $severity = \TYPO3\CMS\Reports\Status::OK;
52 $whereClause = 'username = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr('admin', 'be_users') .
53 BackendUtility::deleteClause('be_users');
54 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid, username, password', 'be_users', $whereClause);
55 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
56 if (!empty($row)) {
57 $secure = TRUE;
58 /** @var $saltingObject \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface */
59 $saltingObject = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($row['password']);
60 if (is_object($saltingObject)) {
61 if ($saltingObject->checkPassword('password', $row['password'])) {
62 $secure = FALSE;
63 }
64 }
65 // Check against plain MD5
66 if ($row['password'] === '5f4dcc3b5aa765d61d8327deb882cf99') {
67 $secure = FALSE;
68 }
69 if (!$secure) {
70 $value = $GLOBALS['LANG']->getLL('status_insecure');
71 $severity = \TYPO3\CMS\Reports\Status::ERROR;
72 $editUserAccountUrl = BackendUtility::getModuleUrl(
73 'record_edit',
74 array(
75 'edit[be_users][' . $row['uid'] . ']' => 'edit',
76 'returnUrl' => BackendUtility::getModuleUrl('system_ReportsTxreportsm1')
77 )
78 );
79 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.backend_admin'),
80 '<a href="' . htmlspecialchars($editUserAccountUrl) . '">', '</a>');
81 }
82 }
83 $GLOBALS['TYPO3_DB']->sql_free_result($res);
84 return GeneralUtility::makeInstance(\TYPO3\CMS\Reports\Status::class,
85 $GLOBALS['LANG']->getLL('status_adminUserAccount'), $value, $message, $severity);
86 }
87
88 /**
89 * Checks whether the encryption key is empty.
90 *
91 * @return \TYPO3\CMS\Reports\Status An object representing whether the encryption key is empty or not
92 */
93 protected function getEncryptionKeyStatus() {
94 $value = $GLOBALS['LANG']->getLL('status_ok');
95 $message = '';
96 $severity = \TYPO3\CMS\Reports\Status::OK;
97 if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
98 $value = $GLOBALS['LANG']->getLL('status_insecure');
99 $severity = \TYPO3\CMS\Reports\Status::ERROR;
100 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
101 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_encryption'),
102 '<a href="' . $url . '">', '</a>');
103 }
104 return GeneralUtility::makeInstance(\TYPO3\CMS\Reports\Status::class,
105 $GLOBALS['LANG']->getLL('status_encryptionKey'), $value, $message, $severity);
106 }
107
108 /**
109 * Checks if fileDenyPattern was changed which is dangerous on Apache
110 *
111 * @return \TYPO3\CMS\Reports\Status An object representing whether the file deny pattern has changed
112 */
113 protected function getFileDenyPatternStatus() {
114 $value = $GLOBALS['LANG']->getLL('status_ok');
115 $message = '';
116 $severity = \TYPO3\CMS\Reports\Status::OK;
117 $defaultParts = GeneralUtility::trimExplode('|', FILE_DENY_PATTERN_DEFAULT, TRUE);
118 $givenParts = GeneralUtility::trimExplode('|', $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'], TRUE);
119 $result = array_intersect($defaultParts, $givenParts);
120 if ($defaultParts !== $result) {
121 $value = $GLOBALS['LANG']->getLL('status_insecure');
122 $severity = \TYPO3\CMS\Reports\Status::ERROR;
123 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
124 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_pattern_partsNotPresent'),
125 '<br /><pre>' . htmlspecialchars(FILE_DENY_PATTERN_DEFAULT) . '</pre><br />');
126 }
127 return GeneralUtility::makeInstance(\TYPO3\CMS\Reports\Status::class,
128 $GLOBALS['LANG']->getLL('status_fileDenyPattern'), $value, $message, $severity);
129 }
130
131 /**
132 * Checks if fileDenyPattern allows to upload .htaccess files which is
133 * dangerous on Apache.
134 *
135 * @return \TYPO3\CMS\Reports\Status An object representing whether it's possible to upload .htaccess files
136 */
137 protected function getHtaccessUploadStatus() {
138 $value = $GLOBALS['LANG']->getLL('status_ok');
139 $message = '';
140 $severity = \TYPO3\CMS\Reports\Status::OK;
141 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] != FILE_DENY_PATTERN_DEFAULT
142 && GeneralUtility::verifyFilenameAgainstDenyPattern('.htaccess')) {
143 $value = $GLOBALS['LANG']->getLL('status_insecure');
144 $severity = \TYPO3\CMS\Reports\Status::ERROR;
145 $message = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_htaccess');
146 }
147 return GeneralUtility::makeInstance(\TYPO3\CMS\Reports\Status::class,
148 $GLOBALS['LANG']->getLL('status_htaccessUploadProtection'), $value, $message, $severity);
149 }
150
151 /**
152 * Checks whether memcached is configured, if that's the case we assume it's also used.
153 *
154 * @return bool TRUE if memcached is used, FALSE otherwise.
155 */
156 protected function isMemcachedUsed() {
157 $memcachedUsed = FALSE;
158 $memcachedServers = $this->getConfiguredMemcachedServers();
159 if (!empty($memcachedServers)) {
160 $memcachedUsed = TRUE;
161 }
162 return $memcachedUsed;
163 }
164
165 /**
166 * Checks whether salted Passwords are configured or not.
167 *
168 * @return \TYPO3\CMS\Reports\Status An object representing the security of the saltedpassswords extension
169 */
170 protected function getSaltedPasswordsStatus() {
171 $value = $GLOBALS['LANG']->getLL('status_ok');
172 $severity = \TYPO3\CMS\Reports\Status::OK;
173 /** @var \TYPO3\CMS\Saltedpasswords\Utility\ExtensionManagerConfigurationUtility $configCheck */
174 $configCheck = GeneralUtility::makeInstance(\TYPO3\CMS\Saltedpasswords\Utility\ExtensionManagerConfigurationUtility::class);
175 $message = '<p>' . $GLOBALS['LANG']->getLL('status_saltedPasswords_infoText') . '</p>';
176 $messageDetail = '';
177 $resultCheck = $configCheck->checkConfigurationBackend(array(), new \TYPO3\CMS\Core\TypoScript\ConfigurationForm());
178 switch ($resultCheck['errorType']) {
179 case FlashMessage::INFO:
180 $messageDetail .= $resultCheck['html'];
181 break;
182 case FlashMessage::WARNING;
183 $severity = \TYPO3\CMS\Reports\Status::WARNING;
184 $messageDetail .= $resultCheck['html'];
185 break;
186 case FlashMessage::ERROR:
187 $value = $GLOBALS['LANG']->getLL('status_insecure');
188 $severity = \TYPO3\CMS\Reports\Status::ERROR;
189 $messageDetail .= $resultCheck['html'];
190 break;
191 default:
192 }
193 $unsecureUserCount = SaltedPasswordsUtility::getNumberOfBackendUsersWithInsecurePassword();
194 if ($unsecureUserCount > 0) {
195 $value = $GLOBALS['LANG']->getLL('status_insecure');
196 $severity = \TYPO3\CMS\Reports\Status::ERROR;
197 $messageDetail .= '<div class="panel panel-warning">' .
198 '<div class="panel-body">' .
199 $GLOBALS['LANG']->getLL('status_saltedPasswords_notAllPasswordsHashed') .
200 '</div>' .
201 '</div>';
202 }
203 $message .= $messageDetail;
204 if (empty($messageDetail)) {
205 $message = '';
206 }
207 return GeneralUtility::makeInstance(\TYPO3\CMS\Reports\Status::class,
208 $GLOBALS['LANG']->getLL('status_saltedPasswords'), $value, $message, $severity);
209 }
210
211 }