Translated using Weblate (Portuguese)
[phpmyadmin.git] / src / Config / ServerConfigChecks.php
blob23ce39c98f45ad320f1b6fe8a90ce7edb9401fea
1 <?php
2 /**
3 * Server config checks management
4 */
6 declare(strict_types=1);
8 namespace PhpMyAdmin\Config;
10 use PhpMyAdmin\Core;
11 use PhpMyAdmin\Sanitize;
12 use PhpMyAdmin\Setup\Index as SetupIndex;
13 use PhpMyAdmin\Url;
15 use function __;
16 use function function_exists;
17 use function htmlspecialchars;
18 use function ini_get;
19 use function mb_strlen;
20 use function sodium_crypto_secretbox_keygen;
21 use function sprintf;
23 use const SODIUM_CRYPTO_SECRETBOX_KEYBYTES;
25 /**
26 * Performs various compatibility, security and consistency checks on current config
28 * Outputs results to message list, must be called between SetupIndex::messagesBegin()
29 * and SetupIndex::messagesEnd()
31 class ServerConfigChecks
33 public function __construct(protected ConfigFile $cfg)
37 /**
38 * Perform config checks
40 public function performConfigChecks(): void
42 /** @var string $blowfishSecret */
43 $blowfishSecret = $this->cfg->get('blowfish_secret', '');
45 $this->performConfigChecksServers($blowfishSecret);
47 // $cfg['AllowArbitraryServer']
48 // should be disabled
49 if ($this->cfg->getValue('AllowArbitraryServer')) {
50 $sAllowArbitraryServerWarn = sprintf(
51 __(
52 'This %soption%s should be disabled as it allows attackers to '
53 . 'bruteforce login to any MySQL server. If you feel this is necessary, '
54 . 'use %srestrict login to MySQL server%s or %strusted proxies list%s. '
55 . 'However, IP-based protection with trusted proxies list may not be '
56 . 'reliable if your IP belongs to an ISP where thousands of users, '
57 . 'including you, are connected to.',
59 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
60 '[/a]',
61 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
62 '[/a]',
63 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
64 '[/a]',
66 SetupIndex::messagesSet(
67 'notice',
68 'AllowArbitraryServer',
69 Descriptions::get('AllowArbitraryServer'),
70 Sanitize::convertBBCode($sAllowArbitraryServerWarn),
74 $this->performConfigChecksLoginCookie();
76 $sDirectoryNotice = __(
77 'This value should be double checked to ensure that this directory is '
78 . 'neither world accessible nor readable or writable by other users on '
79 . 'your server.',
82 // $cfg['SaveDir']
83 // should not be world-accessible
84 if ($this->cfg->getValue('SaveDir') != '') {
85 SetupIndex::messagesSet(
86 'notice',
87 'SaveDir',
88 Descriptions::get('SaveDir'),
89 Sanitize::convertBBCode($sDirectoryNotice),
93 // $cfg['TempDir']
94 // should not be world-accessible
95 if ($this->cfg->getValue('TempDir') != '') {
96 SetupIndex::messagesSet(
97 'notice',
98 'TempDir',
99 Descriptions::get('TempDir'),
100 Sanitize::convertBBCode($sDirectoryNotice),
104 $this->performConfigChecksZips();
108 * Check config of servers
110 * @param string $blowfishSecret Blowfish secret
112 protected function performConfigChecksServers(string $blowfishSecret): void
114 $blowfishSecretSet = false;
116 $serverCnt = $this->cfg->getServerCount();
117 $isCookieAuthUsed = 0;
118 /** @infection-ignore-all */
119 for ($i = 1; $i <= $serverCnt; $i++) {
120 $cookieAuthServer = $this->cfg->getValue('Servers/' . $i . '/auth_type') === 'cookie';
121 $isCookieAuthUsed |= (int) $cookieAuthServer;
122 $serverName = $this->performConfigChecksServersGetServerName(
123 $this->cfg->getServerName($i),
126 $serverName = htmlspecialchars($serverName);
128 if ($cookieAuthServer && mb_strlen($blowfishSecret, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
129 $blowfishSecretSet = true;
130 $this->cfg->set('blowfish_secret', sodium_crypto_secretbox_keygen());
133 // $cfg['Servers'][$i]['ssl']
134 // should be enabled if possible
135 if (! $this->cfg->getValue('Servers/' . $i . '/ssl')) {
136 $title = Descriptions::get('Servers/1/ssl') . ' (' . $serverName . ')';
137 SetupIndex::messagesSet(
138 'notice',
139 'Servers/' . $i . '/ssl',
140 $title,
142 'You should use SSL connections if your database server supports it.',
147 $sSecurityInfoMsg = Sanitize::convertBBCode(sprintf(
149 'If you feel this is necessary, use additional protection settings - '
150 . '%1$shost authentication%2$s settings and %3$strusted proxies list%4$s. '
151 . 'However, IP-based protection may not be reliable if your IP belongs '
152 . 'to an ISP where thousands of users, including you, are connected to.',
154 '[a@' . Url::getCommon(['page' => 'servers', 'mode' => 'edit', 'id' => $i]) . '#tab_Server_config]',
155 '[/a]',
156 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
157 '[/a]',
160 // $cfg['Servers'][$i]['auth_type']
161 // warn about full user credentials if 'auth_type' is 'config'
162 if (
163 $this->cfg->getValue('Servers/' . $i . '/auth_type') === 'config'
164 && $this->cfg->getValue('Servers/' . $i . '/user') != ''
165 && $this->cfg->getValue('Servers/' . $i . '/password') != ''
167 $title = Descriptions::get('Servers/1/auth_type')
168 . ' (' . $serverName . ')';
169 SetupIndex::messagesSet(
170 'notice',
171 'Servers/' . $i . '/auth_type',
172 $title,
173 Sanitize::convertBBCode(sprintf(
175 'You set the [kbd]config[/kbd] authentication type and included '
176 . 'username and password for auto-login, which is not a desirable '
177 . 'option for live hosts. Anyone who knows or guesses your phpMyAdmin '
178 . 'URL can directly access your phpMyAdmin panel. Set %1$sauthentication '
179 . 'type%2$s to [kbd]cookie[/kbd] or [kbd]http[/kbd].',
181 '[a@' . Url::getCommon(['page' => 'servers', 'mode' => 'edit', 'id' => $i]) . '#tab_Server]',
182 '[/a]',
184 . ' ' . $sSecurityInfoMsg,
188 // $cfg['Servers'][$i]['AllowRoot']
189 // $cfg['Servers'][$i]['AllowNoPassword']
190 // serious security flaw
191 if (
192 ! $this->cfg->getValue('Servers/' . $i . '/AllowRoot')
193 || ! $this->cfg->getValue('Servers/' . $i . '/AllowNoPassword')
195 continue;
198 $title = Descriptions::get('Servers/1/AllowNoPassword')
199 . ' (' . $serverName . ')';
200 SetupIndex::messagesSet(
201 'notice',
202 'Servers/' . $i . '/AllowNoPassword',
203 $title,
204 __('You allow for connecting to the server without a password.')
205 . ' ' . $sSecurityInfoMsg,
209 // $cfg['blowfish_secret']
210 // it's required for 'cookie' authentication
211 if ($isCookieAuthUsed === 0 || ! $blowfishSecretSet) {
212 return;
215 // 'cookie' auth used, blowfish_secret was generated
216 SetupIndex::messagesSet(
217 'notice',
218 'blowfish_secret_created',
219 Descriptions::get('blowfish_secret'),
220 Sanitize::convertBBCode(__(
221 'You didn\'t have blowfish secret set and have enabled '
222 . '[kbd]cookie[/kbd] authentication, so a key was automatically '
223 . 'generated for you. It is used to encrypt cookies; you don\'t need to '
224 . 'remember it.',
230 * Define server name
232 * @param string $serverName Server name
233 * @param int $serverId Server id
235 * @return string Server name
237 protected function performConfigChecksServersGetServerName(
238 string $serverName,
239 int $serverId,
240 ): string {
241 if ($serverName === 'localhost') {
242 return $serverName . ' [' . $serverId . ']';
245 return $serverName;
249 * Perform config checks for zip part.
251 protected function performConfigChecksZips(): void
253 $this->performConfigChecksServerGZipdump();
254 $this->performConfigChecksServerBZipdump();
255 $this->performConfigChecksServersZipdump();
259 * Perform config checks for zip part.
261 protected function performConfigChecksServersZipdump(): void
263 // $cfg['ZipDump']
264 // requires zip_open in import
265 if ($this->cfg->getValue('ZipDump') && ! $this->functionExists('zip_open')) {
266 SetupIndex::messagesSet(
267 'error',
268 'ZipDump_import',
269 Descriptions::get('ZipDump'),
270 Sanitize::convertBBCode(sprintf(
272 '%sZip decompression%s requires functions (%s) which are unavailable on this system.',
274 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
275 '[/a]',
276 'zip_open',
281 // $cfg['ZipDump']
282 // requires gzcompress in export
283 if (! $this->cfg->getValue('ZipDump') || $this->functionExists('gzcompress')) {
284 return;
287 SetupIndex::messagesSet(
288 'error',
289 'ZipDump_export',
290 Descriptions::get('ZipDump'),
291 Sanitize::convertBBCode(sprintf(
293 '%sZip compression%s requires functions (%s) which are unavailable on this system.',
295 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
296 '[/a]',
297 'gzcompress',
303 * Check configuration for login cookie
305 protected function performConfigChecksLoginCookie(): void
307 // $cfg['LoginCookieValidity']
308 // value greater than session.gc_maxlifetime will cause
309 // random session invalidation after that time
310 $loginCookieValidity = $this->cfg->getValue('LoginCookieValidity');
311 if ($loginCookieValidity > ini_get('session.gc_maxlifetime')) {
312 SetupIndex::messagesSet(
313 'error',
314 'LoginCookieValidity',
315 Descriptions::get('LoginCookieValidity'),
316 Sanitize::convertBBCode(sprintf(
318 '%1$sLogin cookie validity%2$s greater than %3$ssession.gc_maxlifetime%4$s may '
319 . 'cause random session invalidation (currently session.gc_maxlifetime '
320 . 'is %5$d).',
322 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
323 '[/a]',
324 '[a@' . Core::getPHPDocLink('session.configuration.php#ini.session.gc-maxlifetime') . ']',
325 '[/a]',
326 ini_get('session.gc_maxlifetime'),
331 // $cfg['LoginCookieValidity']
332 // should be at most 1800 (30 min)
333 if ($loginCookieValidity > 1800) {
334 SetupIndex::messagesSet(
335 'notice',
336 'LoginCookieValidity',
337 Descriptions::get('LoginCookieValidity'),
338 Sanitize::convertBBCode(sprintf(
340 '%sLogin cookie validity%s should be set to 1800 seconds (30 minutes) '
341 . 'at most. Values larger than 1800 may pose a security risk such as '
342 . 'impersonation.',
344 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
345 '[/a]',
350 // $cfg['LoginCookieValidity']
351 // $cfg['LoginCookieStore']
352 // LoginCookieValidity must be less or equal to LoginCookieStore
353 if (
354 $this->cfg->getValue('LoginCookieStore') == 0
355 || $loginCookieValidity <= $this->cfg->getValue('LoginCookieStore')
357 return;
360 SetupIndex::messagesSet(
361 'error',
362 'LoginCookieValidity',
363 Descriptions::get('LoginCookieValidity'),
364 Sanitize::convertBBCode(sprintf(
366 'If using [kbd]cookie[/kbd] authentication and %sLogin cookie store%s '
367 . 'is not 0, %sLogin cookie validity%s must be set to a value less or '
368 . 'equal to it.',
370 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
371 '[/a]',
372 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
373 '[/a]',
379 * Check GZipDump configuration
381 protected function performConfigChecksServerBZipdump(): void
383 // $cfg['BZipDump']
384 // requires bzip2 functions
385 if (
386 ! $this->cfg->getValue('BZipDump')
387 || ($this->functionExists('bzopen') && $this->functionExists('bzcompress'))
389 return;
392 $functions = $this->functionExists('bzopen') ? '' : 'bzopen';
393 $functions .= $this->functionExists('bzcompress') ? '' : ($functions !== '' ? ', ' : '') . 'bzcompress';
394 SetupIndex::messagesSet(
395 'error',
396 'BZipDump',
397 Descriptions::get('BZipDump'),
398 Sanitize::convertBBCode(
399 sprintf(
401 '%1$sBzip2 compression and decompression%2$s requires functions (%3$s) which '
402 . 'are unavailable on this system.',
404 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
405 '[/a]',
406 $functions,
413 * Check GZipDump configuration
415 protected function performConfigChecksServerGZipdump(): void
417 // $cfg['GZipDump']
418 // requires zlib functions
419 if (
420 ! $this->cfg->getValue('GZipDump')
421 || ($this->functionExists('gzopen') && $this->functionExists('gzencode'))
423 return;
426 SetupIndex::messagesSet(
427 'error',
428 'GZipDump',
429 Descriptions::get('GZipDump'),
430 Sanitize::convertBBCode(sprintf(
432 '%1$sGZip compression and decompression%2$s requires functions (%3$s) which '
433 . 'are unavailable on this system.',
435 '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
436 '[/a]',
437 'gzencode',
443 * Wrapper around function_exists to allow mock in test
445 * @param string $name Function name
447 protected function functionExists(string $name): bool
449 return function_exists($name);