ServerConfigChecks.class.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Server config checks management
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. /**
  9. * Performs various compatibility, security and consistency checks on current config
  10. *
  11. * Outputs results to message list, must be called between PMA_messagesBegin()
  12. * and PMA_messagesEnd()
  13. *
  14. * @package PhpMyAdmin
  15. */
  16. class ServerConfigChecks
  17. {
  18. /**
  19. * @var ConfigFile configurations being checked
  20. */
  21. protected $cfg;
  22. /**
  23. * Constructor.
  24. *
  25. * @param ConfigFile $cfg Configuration
  26. */
  27. public function __construct(ConfigFile $cfg)
  28. {
  29. $this->cfg = $cfg;
  30. }
  31. /**
  32. * Perform config checks
  33. *
  34. * @return void
  35. */
  36. public function performConfigChecks()
  37. {
  38. $blowfishSecret = $this->cfg->get('blowfish_secret');
  39. $blowfishSecretSet = false;
  40. $cookieAuthUsed = false;
  41. list(
  42. $sAllowArbitraryServerWarn, $sBlowfishSecretMsg,
  43. $sBZipDumpWarn, $sDirectoryNotice, $sForceSSLNotice,
  44. $sGZipDumpWarn, $sLoginCookieValidityWarn,
  45. $sLoginCookieValidityWarn2, $sLoginCookieValidityWarn3,
  46. $sSecurityInfoMsg, $sSrvAuthCfgMsg, $sZipDumpExportWarn,
  47. $sZipDumpImportWarn
  48. ) = self::defineMessages();
  49. list($cookieAuthUsed, $blowfishSecret, $blowfishSecretSet)
  50. = $this->performConfigChecksServers(
  51. $cookieAuthUsed, $blowfishSecret, $sSrvAuthCfgMsg,
  52. $sSecurityInfoMsg, $blowfishSecretSet
  53. );
  54. $this->performConfigChecksCookieAuthUsed(
  55. $cookieAuthUsed, $blowfishSecretSet, $sBlowfishSecretMsg,
  56. $blowfishSecret
  57. );
  58. //
  59. // $cfg['ForceSSL']
  60. // should be enabled if possible
  61. //
  62. if (!$this->cfg->getValue('ForceSSL')) {
  63. PMA_messagesSet(
  64. 'notice',
  65. 'ForceSSL',
  66. PMA_lang(PMA_langName('ForceSSL')),
  67. PMA_lang($sForceSSLNotice)
  68. );
  69. }
  70. //
  71. // $cfg['AllowArbitraryServer']
  72. // should be disabled
  73. //
  74. if ($this->cfg->getValue('AllowArbitraryServer')) {
  75. PMA_messagesSet(
  76. 'notice',
  77. 'AllowArbitraryServer',
  78. PMA_lang(PMA_langName('AllowArbitraryServer')),
  79. PMA_lang($sAllowArbitraryServerWarn)
  80. );
  81. }
  82. $this->performConfigChecksLoginCookie(
  83. $sLoginCookieValidityWarn, $sLoginCookieValidityWarn2,
  84. $sLoginCookieValidityWarn3
  85. );
  86. //
  87. // $cfg['SaveDir']
  88. // should not be world-accessible
  89. //
  90. if ($this->cfg->getValue('SaveDir') != '') {
  91. PMA_messagesSet(
  92. 'notice',
  93. 'SaveDir',
  94. PMA_lang(PMA_langName('SaveDir')),
  95. PMA_lang($sDirectoryNotice)
  96. );
  97. }
  98. //
  99. // $cfg['TempDir']
  100. // should not be world-accessible
  101. //
  102. if ($this->cfg->getValue('TempDir') != '') {
  103. PMA_messagesSet(
  104. 'notice',
  105. 'TempDir',
  106. PMA_lang(PMA_langName('TempDir')),
  107. PMA_lang($sDirectoryNotice)
  108. );
  109. }
  110. $this->performConfigChecksZips(
  111. $sGZipDumpWarn, $sBZipDumpWarn, $sZipDumpImportWarn,
  112. $sZipDumpExportWarn
  113. );
  114. }
  115. /**
  116. * Check config of servers
  117. *
  118. * @param boolean $cookieAuthUsed Cookie auth is used
  119. * @param string $blowfishSecret Blowfish secret
  120. * @param string $sServerAuthCfgMsg Message for server auth config
  121. * @param string $sSecurityInfoMsg Message for security information
  122. * @param boolean $blowfishSecretSet Blowfish secret set
  123. *
  124. * @return array
  125. */
  126. protected function performConfigChecksServers(
  127. $cookieAuthUsed, $blowfishSecret, $sServerAuthCfgMsg,
  128. $sSecurityInfoMsg, $blowfishSecretSet
  129. ) {
  130. $serverCnt = $this->cfg->getServerCount();
  131. for ($i = 1; $i <= $serverCnt; $i++) {
  132. $cookieAuthServer
  133. = ($this->cfg->getValue("Servers/$i/auth_type") == 'cookie');
  134. $cookieAuthUsed |= $cookieAuthServer;
  135. $serverName = $this->performConfigChecksServersGetServerName(
  136. $this->cfg->getServerName($i), $i
  137. );
  138. $serverName = htmlspecialchars($serverName);
  139. list($blowfishSecret, $blowfishSecretSet)
  140. = $this->performConfigChecksServersSetBlowfishSecret(
  141. $blowfishSecret, $cookieAuthServer, $blowfishSecretSet
  142. );
  143. //
  144. // $cfg['Servers'][$i]['ssl']
  145. // should be enabled if possible
  146. //
  147. if (!$this->cfg->getValue("Servers/$i/ssl")) {
  148. $title = PMA_lang(PMA_langName('Servers/1/ssl')) . " ($serverName)";
  149. PMA_messagesSet(
  150. 'notice',
  151. "Servers/$i/ssl",
  152. $title,
  153. __('You should use SSL connections if your database server supports it.')
  154. );
  155. }
  156. //
  157. // $cfg['Servers'][$i]['auth_type']
  158. // warn about full user credentials if 'auth_type' is 'config'
  159. //
  160. if ($this->cfg->getValue("Servers/$i/auth_type") == 'config'
  161. && $this->cfg->getValue("Servers/$i/user") != ''
  162. && $this->cfg->getValue("Servers/$i/password") != ''
  163. ) {
  164. $title = PMA_lang(PMA_langName('Servers/1/auth_type'))
  165. . " ($serverName)";
  166. PMA_messagesSet(
  167. 'notice',
  168. "Servers/$i/auth_type",
  169. $title,
  170. PMA_lang($sServerAuthCfgMsg, $i) . ' '
  171. . PMA_lang($sSecurityInfoMsg, $i)
  172. );
  173. }
  174. //
  175. // $cfg['Servers'][$i]['AllowRoot']
  176. // $cfg['Servers'][$i]['AllowNoPassword']
  177. // serious security flaw
  178. //
  179. if ($this->cfg->getValue("Servers/$i/AllowRoot")
  180. && $this->cfg->getValue("Servers/$i/AllowNoPassword")
  181. ) {
  182. $title = PMA_lang(PMA_langName('Servers/1/AllowNoPassword'))
  183. . " ($serverName)";
  184. PMA_messagesSet(
  185. 'notice',
  186. "Servers/$i/AllowNoPassword",
  187. $title,
  188. __('You allow for connecting to the server without a password.') . ' '
  189. . PMA_lang($sSecurityInfoMsg, $i)
  190. );
  191. }
  192. }
  193. return array($cookieAuthUsed, $blowfishSecret, $blowfishSecretSet);
  194. }
  195. /**
  196. * Set blowfish secret
  197. *
  198. * @param string $blowfishSecret Blowfish secret
  199. * @param boolean $cookieAuthServer Cookie auth is used
  200. * @param boolean $blowfishSecretSet Blowfish secret set
  201. *
  202. * @return array
  203. */
  204. protected function performConfigChecksServersSetBlowfishSecret(
  205. $blowfishSecret, $cookieAuthServer, $blowfishSecretSet
  206. ) {
  207. if ($cookieAuthServer && $blowfishSecret === null) {
  208. $blowfishSecret = uniqid('', true);
  209. $blowfishSecretSet = true;
  210. $this->cfg->set('blowfish_secret', $blowfishSecret);
  211. return array($blowfishSecret, $blowfishSecretSet);
  212. }
  213. return array($blowfishSecret, $blowfishSecretSet);
  214. }
  215. /**
  216. * Define server name
  217. *
  218. * @param string $serverName Server name
  219. * @param int $serverId Server id
  220. *
  221. * @return string Server name
  222. */
  223. protected function performConfigChecksServersGetServerName(
  224. $serverName, $serverId
  225. ) {
  226. if ($serverName == 'localhost') {
  227. $serverName .= " [$serverId]";
  228. return $serverName;
  229. }
  230. return $serverName;
  231. }
  232. /**
  233. * Perform config checks for zip part.
  234. *
  235. * @param string $sGZipDumpWarning Gzip dump warning
  236. * @param string $sBZipDumpWarning Bzip dump warning
  237. * @param string $sZipDumpImportWarn Zip dump import warning
  238. * @param string $sZipDumpExportWarn Zip dump export warning
  239. *
  240. * @return void
  241. */
  242. protected function performConfigChecksZips(
  243. $sGZipDumpWarning, $sBZipDumpWarning, $sZipDumpImportWarn,
  244. $sZipDumpExportWarn
  245. ) {
  246. $this->performConfigChecksServerGZipdump($sGZipDumpWarning);
  247. $this->performConfigChecksServerBZipdump($sBZipDumpWarning);
  248. $this->performConfigChecksServersZipdump(
  249. $sZipDumpImportWarn, $sZipDumpExportWarn
  250. );
  251. }
  252. /**
  253. * Perform config checks for zip part.
  254. *
  255. * @param string $sZipDumpImportWarn Zip dump import warning
  256. * @param string $sZipDumpExportWarn Zip dump export warning
  257. *
  258. * @return void
  259. */
  260. protected function performConfigChecksServersZipdump(
  261. $sZipDumpImportWarn, $sZipDumpExportWarn
  262. ) {
  263. //
  264. // $cfg['ZipDump']
  265. // requires zip_open in import
  266. //
  267. if ($this->cfg->getValue('ZipDump') && !@function_exists('zip_open')) {
  268. PMA_messagesSet(
  269. 'error',
  270. 'ZipDump_import',
  271. PMA_lang(PMA_langName('ZipDump')),
  272. PMA_lang($sZipDumpImportWarn, 'zip_open')
  273. );
  274. }
  275. //
  276. // $cfg['ZipDump']
  277. // requires gzcompress in export
  278. //
  279. if ($this->cfg->getValue('ZipDump') && !@function_exists('gzcompress')) {
  280. PMA_messagesSet(
  281. 'error',
  282. 'ZipDump_export',
  283. PMA_lang(PMA_langName('ZipDump')),
  284. PMA_lang($sZipDumpExportWarn, 'gzcompress')
  285. );
  286. }
  287. }
  288. /**
  289. * Check config of servers
  290. *
  291. * @param boolean $cookieAuthUsed Cookie auth is used
  292. * @param boolean $blowfishSecretSet Blowfish secret set
  293. * @param string $sBlowfishSecretMsg Blowfish secret message
  294. * @param string $blowfishSecret Blowfish secret
  295. *
  296. * @return array
  297. */
  298. protected function performConfigChecksCookieAuthUsed(
  299. $cookieAuthUsed, $blowfishSecretSet, $sBlowfishSecretMsg,
  300. $blowfishSecret
  301. ) {
  302. //
  303. // $cfg['blowfish_secret']
  304. // it's required for 'cookie' authentication
  305. //
  306. if ($cookieAuthUsed) {
  307. if ($blowfishSecretSet) {
  308. // 'cookie' auth used, blowfish_secret was generated
  309. PMA_messagesSet(
  310. 'notice',
  311. 'blowfish_secret_created',
  312. PMA_lang(PMA_langName('blowfish_secret')),
  313. PMA_lang($sBlowfishSecretMsg)
  314. );
  315. } else {
  316. $blowfishWarnings = array();
  317. // check length
  318. if (strlen($blowfishSecret) < 8) {
  319. // too short key
  320. $blowfishWarnings[] = __('Key is too short, it should have at least 8 characters.');
  321. }
  322. // check used characters
  323. $hasDigits = (bool)preg_match('/\d/', $blowfishSecret);
  324. $hasChars = (bool)preg_match('/\S/', $blowfishSecret);
  325. $hasNonword = (bool)preg_match('/\W/', $blowfishSecret);
  326. if (!$hasDigits || !$hasChars || !$hasNonword) {
  327. $blowfishWarnings[] = PMA_lang(__('Key should contain letters, numbers [em]and[/em] special characters.'));
  328. }
  329. if (!empty($blowfishWarnings)) {
  330. PMA_messagesSet(
  331. 'error',
  332. 'blowfish_warnings' . count($blowfishWarnings),
  333. PMA_lang(PMA_langName('blowfish_secret')),
  334. implode('<br />', $blowfishWarnings)
  335. );
  336. }
  337. }
  338. }
  339. }
  340. /**
  341. * Define all messages
  342. *
  343. * @return array
  344. */
  345. protected static function defineMessages()
  346. {
  347. $sAllowArbitraryServerWarn = __('This %soption%s should be disabled as it allows attackers to bruteforce login to any MySQL server. If you feel this is necessary, use %strusted proxies list%s. However, IP-based protection may not be reliable if your IP belongs to an ISP where thousands of users, including you, are connected to.');
  348. $sAllowArbitraryServerWarn = sprintf(
  349. $sAllowArbitraryServerWarn,
  350. '[a@?page=form&amp;formset=Features#tab_Security]',
  351. '[/a]', '[a@?page=form&amp;formset=Features#tab_Security]',
  352. '[/a]'
  353. );
  354. $sBlowfishSecretMsg = __('You didn\'t have blowfish secret set and have enabled [kbd]cookie[/kbd] authentication, so a key was automatically generated for you. It is used to encrypt cookies; you don\'t need to remember it.');
  355. $sBZipDumpWarning = __('%sBzip2 compression and decompression%s requires functions (%s) which are unavailable on this system.');
  356. $sBZipDumpWarning = sprintf(
  357. $sBZipDumpWarning,
  358. '[a@?page=form&amp;formset=Features#tab_Import_export]',
  359. '[/a]', '%s'
  360. );
  361. $sDirectoryNotice = __('This value should be double checked to ensure that this directory is neither world accessible nor readable or writable by other users on your server.');
  362. $sForceSSLNotice = __('This %soption%s should be enabled if your web server supports it.');
  363. $sForceSSLNotice = sprintf(
  364. $sForceSSLNotice,
  365. '[a@?page=form&amp;formset=Features#tab_Security]',
  366. '[/a]'
  367. );
  368. $sGZipDumpWarning = __('%sGZip compression and decompression%s requires functions (%s) which are unavailable on this system.');
  369. $sGZipDumpWarning = sprintf(
  370. $sGZipDumpWarning,
  371. '[a@?page=form&amp;formset=Features#tab_Import_export]',
  372. '[/a]',
  373. '%s'
  374. );
  375. $sLoginCookieValidityWarn = __('%sLogin cookie validity%s greater than %ssession.gc_maxlifetime%s may cause random session invalidation (currently session.gc_maxlifetime is %d).');
  376. $sLoginCookieValidityWarn = sprintf(
  377. $sLoginCookieValidityWarn,
  378. '[a@?page=form&amp;formset=Features#tab_Security]',
  379. '[/a]',
  380. '[a@' . PMA_getPHPDocLink(
  381. 'session.configuration.php#ini.session.gc-maxlifetime'
  382. ) . ']',
  383. '[/a]',
  384. ini_get('session.gc_maxlifetime')
  385. );
  386. $sLoginCookieValidityWarn2 = __('%sLogin cookie validity%s should be set to 1800 seconds (30 minutes) at most. Values larger than 1800 may pose a security risk such as impersonation.');
  387. $sLoginCookieValidityWarn2 = sprintf(
  388. $sLoginCookieValidityWarn2,
  389. '[a@?page=form&amp;formset=Features#tab_Security]',
  390. '[/a]'
  391. );
  392. $sLoginCookieValidityWarn3 = __('If using [kbd]cookie[/kbd] authentication and %sLogin cookie store%s is not 0, %sLogin cookie validity%s must be set to a value less or equal to it.');
  393. $sLoginCookieValidityWarn3 = sprintf(
  394. $sLoginCookieValidityWarn3,
  395. '[a@?page=form&amp;formset=Features#tab_Security]',
  396. '[/a]', '[a@?page=form&amp;formset=Features#tab_Security]',
  397. '[/a]'
  398. );
  399. $sSecurityInfoMsg = __('If you feel this is necessary, use additional protection settings - %shost authentication%s settings and %strusted proxies list%s. However, IP-based protection may not be reliable if your IP belongs to an ISP where thousands of users, including you, are connected to.');
  400. $sSecurityInfoMsg = sprintf(
  401. $sSecurityInfoMsg,
  402. '[a@?page=servers&amp;mode=edit&amp;id=%1$d#tab_Server_config]',
  403. '[/a]',
  404. '[a@?page=form&amp;formset=Features#tab_Security]',
  405. '[/a]'
  406. );
  407. $sServerAuthConfigMsg = __('You set the [kbd]config[/kbd] authentication type and included username and password for auto-login, which is not a desirable option for live hosts. Anyone who knows or guesses your phpMyAdmin URL can directly access your phpMyAdmin panel. Set %sauthentication type%s to [kbd]cookie[/kbd] or [kbd]http[/kbd].');
  408. $sServerAuthConfigMsg = sprintf(
  409. $sServerAuthConfigMsg,
  410. '[a@?page=servers&amp;mode=edit&amp;id=%1$d#tab_Server]',
  411. '[/a]'
  412. );
  413. $sZipDumpExportWarn = __('%sZip compression%s requires functions (%s) which are unavailable on this system.');
  414. $sZipDumpExportWarn = sprintf(
  415. $sZipDumpExportWarn,
  416. '[a@?page=form&amp;formset=Features#tab_Import_export]',
  417. '[/a]',
  418. '%s'
  419. );
  420. $sZipDumpImportWarn = __('%sZip decompression%s requires functions (%s) which are unavailable on this system.');
  421. $sZipDumpImportWarn = sprintf(
  422. $sZipDumpImportWarn,
  423. '[a@?page=form&amp;formset=Features#tab_Import_export]',
  424. '[/a]',
  425. '%s'
  426. );
  427. return array(
  428. $sAllowArbitraryServerWarn, $sBlowfishSecretMsg, $sBZipDumpWarning,
  429. $sDirectoryNotice, $sForceSSLNotice, $sGZipDumpWarning,
  430. $sLoginCookieValidityWarn, $sLoginCookieValidityWarn2,
  431. $sLoginCookieValidityWarn3, $sSecurityInfoMsg, $sServerAuthConfigMsg,
  432. $sZipDumpExportWarn, $sZipDumpImportWarn
  433. );
  434. }
  435. /**
  436. * Check configuration for login cookie
  437. *
  438. * @param string $sLoginCookieValidityWarn Warning 1 for login cookie validity
  439. * @param string $sLoginCookieValidityWarn2 Warning 2 for login cookie validity
  440. * @param string $sLoginCookieValidityWarn3 Warning 3 for login cookie validity
  441. *
  442. * @return void
  443. */
  444. protected function performConfigChecksLoginCookie(
  445. $sLoginCookieValidityWarn, $sLoginCookieValidityWarn2,
  446. $sLoginCookieValidityWarn3
  447. ) {
  448. //
  449. // $cfg['LoginCookieValidity']
  450. // value greater than session.gc_maxlifetime will cause
  451. // random session invalidation after that time
  452. $loginCookieValidity = $this->cfg->getValue('LoginCookieValidity');
  453. if ($loginCookieValidity > ini_get('session.gc_maxlifetime')
  454. ) {
  455. PMA_messagesSet(
  456. 'error',
  457. 'LoginCookieValidity',
  458. PMA_lang(PMA_langName('LoginCookieValidity')),
  459. PMA_lang($sLoginCookieValidityWarn)
  460. );
  461. }
  462. //
  463. // $cfg['LoginCookieValidity']
  464. // should be at most 1800 (30 min)
  465. //
  466. if ($loginCookieValidity > 1800) {
  467. PMA_messagesSet(
  468. 'notice',
  469. 'LoginCookieValidity',
  470. PMA_lang(PMA_langName('LoginCookieValidity')),
  471. PMA_lang($sLoginCookieValidityWarn2)
  472. );
  473. }
  474. //
  475. // $cfg['LoginCookieValidity']
  476. // $cfg['LoginCookieStore']
  477. // LoginCookieValidity must be less or equal to LoginCookieStore
  478. //
  479. if (($this->cfg->getValue('LoginCookieStore') != 0)
  480. && ($loginCookieValidity > $this->cfg->getValue('LoginCookieStore'))
  481. ) {
  482. PMA_messagesSet(
  483. 'error',
  484. 'LoginCookieValidity',
  485. PMA_lang(PMA_langName('LoginCookieValidity')),
  486. PMA_lang($sLoginCookieValidityWarn3)
  487. );
  488. }
  489. }
  490. /**
  491. * Check GZipDump configuration
  492. *
  493. * @param string $sBZipDumpWarn Warning for BZipDumpWarning
  494. *
  495. * @return void
  496. */
  497. protected function performConfigChecksServerBZipdump($sBZipDumpWarn)
  498. {
  499. //
  500. // $cfg['BZipDump']
  501. // requires bzip2 functions
  502. //
  503. if ($this->cfg->getValue('BZipDump')
  504. && (!@function_exists('bzopen') || !@function_exists('bzcompress'))
  505. ) {
  506. $functions = @function_exists('bzopen')
  507. ? '' :
  508. 'bzopen';
  509. $functions .= @function_exists('bzcompress')
  510. ? ''
  511. : ($functions ? ', ' : '') . 'bzcompress';
  512. PMA_messagesSet(
  513. 'error',
  514. 'BZipDump',
  515. PMA_lang(PMA_langName('BZipDump')),
  516. PMA_lang($sBZipDumpWarn, $functions)
  517. );
  518. }
  519. }
  520. /**
  521. * Check GZipDump configuration
  522. *
  523. * @param string $sGZipDumpWarn Warning for GZipDumpWarning
  524. *
  525. * @return void
  526. */
  527. protected function performConfigChecksServerGZipdump($sGZipDumpWarn)
  528. {
  529. //
  530. // $cfg['GZipDump']
  531. // requires zlib functions
  532. //
  533. if ($this->cfg->getValue('GZipDump')
  534. && (@!function_exists('gzopen') || @!function_exists('gzencode'))
  535. ) {
  536. PMA_messagesSet(
  537. 'error',
  538. 'GZipDump',
  539. PMA_lang(PMA_langName('GZipDump')),
  540. PMA_lang($sGZipDumpWarn, 'gzencode')
  541. );
  542. }
  543. }
  544. }