Skip to content

Commit

Permalink
Merge remote-tracking branch 'security/QA_5_1-security' into QA_5_1
Browse files Browse the repository at this point in the history
  • Loading branch information
ibennetch committed Jan 20, 2022
2 parents 37f6089 + 813ffbf commit 3c91707
Show file tree
Hide file tree
Showing 69 changed files with 1,137 additions and 297 deletions.
8 changes: 5 additions & 3 deletions composer.json
Expand Up @@ -67,7 +67,7 @@
"pragmarx/google2fa-qrcode": "<1.0.1"
},
"suggest": {
"ext-openssl": "Cookie encryption",
"ext-openssl": "For encryption performance",
"ext-curl": "Updates checking",
"ext-opcache": "Better performance",
"ext-zlib": "For gz import and export",
Expand All @@ -77,17 +77,19 @@
"ext-mbstring": "For best performance",
"tecnickcom/tcpdf": "For PDF support",
"pragmarx/google2fa-qrcode": "For 2FA authentication",
"code-lts/u2f-php-server": "For FIDO U2F authentication"
"code-lts/u2f-php-server": "For FIDO U2F authentication",
"paragonie/sodium_compat": "For modern encryption support"
},
"require-dev": {
"code-lts/u2f-php-server": "^1.2",
"paragonie/sodium_compat": "^1.17",
"php-webdriver/webdriver": "^1.11",
"phpmyadmin/coding-standard": "^2.1.1",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.3",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^7.5 || ^8.0 || ^9.0",
"pragmarx/google2fa-qrcode": "^1.0.1",
"code-lts/u2f-php-server": "^1.2",
"symfony/console": "^4.4",
"symfony/finder": "^4.4",
"symfony/twig-bridge": "^4.4",
Expand Down
7 changes: 7 additions & 0 deletions config.sample.inc.php
Expand Up @@ -147,6 +147,13 @@
*/
//$cfg['SendErrorReports'] = 'always';

/**
* 'URLQueryEncryption' defines whether phpMyAdmin will encrypt sensitive data from the URL query string.
* 'URLQueryEncryptionSecretKey' is a 32 bytes long secret key used to encrypt/decrypt the URL query string.
*/
//$cfg['URLQueryEncryption'] = true;
//$cfg['URLQueryEncryptionSecretKey'] = '';

/**
* You can find more configuration options in the documentation
* in the doc/ folder or at <https://docs.phpmyadmin.net/>.
Expand Down
35 changes: 35 additions & 0 deletions doc/config.rst
Expand Up @@ -1498,6 +1498,20 @@ Server connection settings
after logout (doesn't affect config authentication method). Should be
absolute including protocol.

.. config:option:: $cfg['Servers'][$i]['hide_connection_errors']
:type: boolean
:default: false

.. versionadded:: 4.9.8

Whether to show or hide detailed MySQL/MariaDB connection errors on the login page.

.. note::

This error message can contain the target database server hostname or IP address,
which may reveal information about your network to an attacker.

Generic settings
----------------

Expand Down Expand Up @@ -1787,6 +1801,27 @@ Generic settings
When enabled, a user can drag a file in to their browser and phpMyAdmin will
attempt to import the file.

.. config:option:: $cfg['URLQueryEncryption']
:type: boolean
:default: false

.. versionadded:: 4.9.8

Define whether phpMyAdmin will encrypt sensitive data (like database name
and table name) from the URL query string. Default is to not encrypt the URL
query string.

.. config:option:: $cfg['URLQueryEncryptionSecretKey']
:type: string
:default: ``''``

.. versionadded:: 4.9.8

A secret key used to encrypt/decrypt the URL query string.
Should be 32 bytes long.

Cookie authentication options
-----------------------------

Expand Down
2 changes: 1 addition & 1 deletion js/src/navigation.js
Expand Up @@ -163,7 +163,7 @@ Navigation.loadChildNodes = function (isNode, $expandElem, callback) {
};
}

$.get('index.php?route=/navigation&ajax_request=1', params, function (data) {
$.post('index.php?route=/navigation&ajax_request=1', params, function (data) {
if (typeof data !== 'undefined' && data.success === true) {
$destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there
if (isNode) {
Expand Down
9 changes: 6 additions & 3 deletions libraries/classes/Controllers/Setup/ConfigController.php
Expand Up @@ -5,8 +5,8 @@
namespace PhpMyAdmin\Controllers\Setup;

use PhpMyAdmin\Config\FormDisplayTemplate;
use PhpMyAdmin\Core;
use PhpMyAdmin\Setup\ConfigGenerator;
use function is_string;

class ConfigController extends AbstractController
{
Expand All @@ -17,6 +17,9 @@ class ConfigController extends AbstractController
*/
public function index(array $params): string
{
$formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : '';
$eol = isset($params['eol']) && $params['eol'] === 'win' ? 'win' : 'unix';

$pages = $this->getPages();

$formDisplayTemplate = new FormDisplayTemplate($GLOBALS['PMA_Config']);
Expand All @@ -34,13 +37,13 @@ public function index(array $params): string
$config = ConfigGenerator::getConfigFile($this->config);

return $this->template->render('setup/config/index', [
'formset' => $params['formset'] ?? '',
'formset' => $formset,
'pages' => $pages,
'form_top_html' => $formTop,
'fieldset_top_html' => $fieldsetTop,
'form_bottom_html' => $formBottom,
'fieldset_bottom_html' => $fieldsetBottom,
'eol' => Core::ifSetOr($params['eol'], 'unix'),
'eol' => $eol,
'config' => $config,
]);
}
Expand Down
5 changes: 3 additions & 2 deletions libraries/classes/Controllers/Setup/FormController.php
Expand Up @@ -10,6 +10,7 @@
use PhpMyAdmin\Setup\FormProcessing;
use function ob_get_clean;
use function ob_start;
use function is_string;

class FormController extends AbstractController
{
Expand All @@ -22,7 +23,7 @@ public function index(array $params): string
{
$pages = $this->getPages();

$formset = Core::isValid($params['formset'], 'scalar') ? $params['formset'] : null;
$formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : '';

$formClass = SetupFormList::get($formset);
if ($formClass === null) {
Expand All @@ -36,7 +37,7 @@ public function index(array $params): string
$page = ob_get_clean();

return $this->template->render('setup/form/index', [
'formset' => $params['formset'] ?? '',
'formset' => $formset,
'pages' => $pages,
'name' => $form::getName(),
'page' => $page,
Expand Down
49 changes: 4 additions & 45 deletions libraries/classes/Controllers/Setup/HomeController.php
Expand Up @@ -9,10 +9,8 @@
use PhpMyAdmin\Config\ServerConfigChecks;
use PhpMyAdmin\Core;
use PhpMyAdmin\LanguageManager;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Setup\Index;
use function preg_replace;
use function uniqid;
use function is_string;

class HomeController extends AbstractController
{
Expand All @@ -23,11 +21,9 @@ class HomeController extends AbstractController
*/
public function index(array $params): string
{
$pages = $this->getPages();
$formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : '';

// Handle done action info
$actionDone = Core::isValid($params['action_done'], 'scalar') ? $params['action_done'] : '';
$actionDone = preg_replace('/[^a-z_]/', '', $actionDone);
$pages = $this->getPages();

// message handling
Index::messagesBegin();
Expand All @@ -53,43 +49,6 @@ public function index(array $params): string
$text .= '</a>';
Index::messagesSet('notice', 'no_https', __('Insecure connection'), $text);

// Check for done action info and set notice message if present
switch ($actionDone) {
case 'config_saved':
/* Use uniqid to display this message every time configuration is saved */
Index::messagesSet(
'notice',
uniqid('config_saved'),
__('Configuration saved.'),
Sanitize::sanitizeMessage(
__(
'Configuration saved to file config/config.inc.php in phpMyAdmin '
. 'top level directory, copy it to top level one and delete '
. 'directory config to use it.'
)
)
);
break;
case 'config_not_saved':
/* Use uniqid to display this message every time configuration is saved */
Index::messagesSet(
'notice',
uniqid('config_not_saved'),
__('Configuration not saved!'),
Sanitize::sanitizeMessage(
__(
'Please create web server writable folder [em]config[/em] in '
. 'phpMyAdmin top level directory as described in '
. '[doc@setup_script]documentation[/doc]. Otherwise you will be '
. 'only able to download or display it.'
)
)
);
break;
default:
break;
}

Index::messagesEnd();
$messages = Index::messagesShowHtml();

Expand Down Expand Up @@ -205,7 +164,7 @@ public function index(array $params): string
);

return $this->template->render('setup/home/index', [
'formset' => $params['formset'] ?? '',
'formset' => $formset,
'languages' => $languages,
'messages' => $messages,
'servers_form_top_html' => $serversFormTopHtml,
Expand Down
24 changes: 16 additions & 8 deletions libraries/classes/Controllers/Setup/ServersController.php
Expand Up @@ -5,10 +5,12 @@
namespace PhpMyAdmin\Controllers\Setup;

use PhpMyAdmin\Config\Forms\Setup\ServersForm;
use PhpMyAdmin\Core;
use PhpMyAdmin\Setup\FormProcessing;
use function ob_get_clean;
use function ob_start;
use function is_string;
use function is_numeric;
use function in_array;

class ServersController extends AbstractController
{
Expand All @@ -19,12 +21,18 @@ class ServersController extends AbstractController
*/
public function index(array $params): string
{
$formset = isset($params['formset']) && is_string($params['formset']) ? $params['formset'] : '';
$id = isset($params['id']) && is_numeric($params['id']) && (int) $params['id'] >= 1 ? (int) $params['id'] : 0;
$mode = '';
if (isset($params['mode']) && in_array($params['mode'], ['add', 'edit', 'revert'], true)) {
$mode = $params['mode'];
}

$pages = $this->getPages();

$id = Core::isValid($params['id'], 'numeric') ? (int) $params['id'] : null;
$hasServer = ! empty($id) && $this->config->get('Servers/' . $id) !== null;
$hasServer = $id >= 1 && $this->config->get('Servers/' . $id) !== null;

if (! $hasServer && ($params['mode'] !== 'revert' && $params['mode'] !== 'edit')) {
if (! $hasServer && $mode !== 'revert' && $mode !== 'edit') {
$id = 0;
}

Expand All @@ -33,10 +41,10 @@ public function index(array $params): string
$page = ob_get_clean();

return $this->template->render('setup/servers/index', [
'formset' => $params['formset'] ?? '',
'formset' => $formset,
'pages' => $pages,
'has_server' => $hasServer,
'mode' => $params['mode'],
'mode' => $mode,
'server_id' => $id,
'server_dsn' => $this->config->getServerDSN($id),
'page' => $page,
Expand All @@ -48,9 +56,9 @@ public function index(array $params): string
*/
public function destroy(array $params): void
{
$id = Core::isValid($params['id'], 'numeric') ? (int) $params['id'] : null;
$id = isset($params['id']) && is_numeric($params['id']) && (int) $params['id'] >= 1 ? (int) $params['id'] : 0;

$hasServer = ! empty($id) && $this->config->get('Servers/' . $id) !== null;
$hasServer = $id >= 1 && $this->config->get('Servers/' . $id) !== null;

if (! $hasServer) {
return;
Expand Down
34 changes: 34 additions & 0 deletions libraries/classes/Core.php
Expand Up @@ -71,6 +71,7 @@
use function unserialize;
use function urldecode;
use function vsprintf;
use function json_decode;

/**
* Core class
Expand Down Expand Up @@ -1426,4 +1427,37 @@ public static function getContainerBuilder(): ContainerBuilder

return $containerBuilder;
}

/**
* @return void
*/
public static function populateRequestWithEncryptedQueryParams()
{
if (
(! isset($_GET['eq']) || ! is_string($_GET['eq']))
&& (! isset($_POST['eq']) || ! is_string($_POST['eq']))
) {
unset($_GET['eq'], $_POST['eq'], $_REQUEST['eq']);

return;
}

$isFromPost = isset($_POST['eq']);
$decryptedQuery = Url::decryptQuery($isFromPost ? $_POST['eq'] : $_GET['eq']);
unset($_GET['eq'], $_POST['eq'], $_REQUEST['eq']);
if ($decryptedQuery === null) {
return;
}

$urlQueryParams = (array) json_decode($decryptedQuery);
foreach ($urlQueryParams as $urlQueryParamKey => $urlQueryParamValue) {
if ($isFromPost) {
$_POST[$urlQueryParamKey] = $urlQueryParamValue;
} else {
$_GET[$urlQueryParamKey] = $urlQueryParamValue;
}

$_REQUEST[$urlQueryParamKey] = $urlQueryParamValue;
}
}
}

0 comments on commit 3c91707

Please sign in to comment.