diff options
author | MaurĂcio Meneghini Fauth <mauricio@fauth.dev> | 2022-01-10 19:33:03 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-10 19:33:03 +0300 |
commit | ae11d5260b4bde42100c8696218a2bfd11a2d740 (patch) | |
tree | 325cd230d1a6b711cdc35dfdc36fa3d55b289cb7 /libraries | |
parent | bfd335bfd4ebff6634a5492d967253c6cf89792f (diff) | |
parent | ede23967967ac4e715b2df392a1388c2fb69942b (diff) |
Merge pull request #334 from phpmyadmin/security/315
Encrypt the URL query when sensitive data is present
Diffstat (limited to 'libraries')
25 files changed, 484 insertions, 154 deletions
diff --git a/libraries/classes/Controllers/Server/ServerBinlogController.php b/libraries/classes/Controllers/Server/ServerBinlogController.php index 57983ec3d2..807b7223bf 100644 --- a/libraries/classes/Controllers/Server/ServerBinlogController.php +++ b/libraries/classes/Controllers/Server/ServerBinlogController.php @@ -193,7 +193,7 @@ class ServerBinlogController extends Controller } $html .= '<a href="server_binlog.php" data-post="' - . Url::getCommon($this_url_params, '') . '"'; + . Url::getCommon($this_url_params, '', false) . '"'; if (Util::showIcons('TableNavigationLinksMode')) { $html .= ' title="' . _pgettext('Previous page', 'Previous') . '">'; } else { @@ -215,7 +215,7 @@ class ServerBinlogController extends Controller $tempTitle = __('Show Full Queries'); $tempImgMode = 'full'; } - $html .= '<a href="server_binlog.php" data-post="' . Url::getCommon($this_url_params, '') + $html .= '<a href="server_binlog.php" data-post="' . Url::getCommon($this_url_params, '', false) . '" title="' . $tempTitle . '">' . '<img src="' . $GLOBALS['pmaThemeImage'] . 's_' . $tempImgMode . 'text.png" alt="' . $tempTitle . '" /></a>'; @@ -226,7 +226,7 @@ class ServerBinlogController extends Controller $this_url_params = $url_params; $this_url_params['pos'] = $pos + $GLOBALS['cfg']['MaxRows']; $html .= ' - <a href="server_binlog.php" data-post="' - . Url::getCommon($this_url_params, '') + . Url::getCommon($this_url_params, '', false) . '"'; if (Util::showIcons('TableNavigationLinksMode')) { $html .= ' title="' . _pgettext('Next page', 'Next') . '">'; diff --git a/libraries/classes/Controllers/Table/TableStructureController.php b/libraries/classes/Controllers/Table/TableStructureController.php index 5ace136ab6..80dccdb8e5 100644 --- a/libraries/classes/Controllers/Table/TableStructureController.php +++ b/libraries/classes/Controllers/Table/TableStructureController.php @@ -1241,13 +1241,6 @@ class TableStructureController extends TableController 'DistinctValues' => Util::getIcon('b_browse', __('Distinct values')), ); - $edit_view_url = ''; - if ($this->_tbl_is_view && ! $this->_db_is_system_schema) { - $edit_view_url = Url::getCommon( - array('db' => $this->db, 'table' => $this->table) - ); - } - /** * Displays Space usage and row statistics */ @@ -1275,11 +1268,11 @@ class TableStructureController extends TableController 'tbl_is_view' => $this->_tbl_is_view, 'mime_map' => $mime_map, 'url_query' => $this->_url_query, + 'url_params' => $url_params, 'titles' => $titles, 'tbl_storage_engine' => $this->_tbl_storage_engine, 'primary' => $primary_index, 'columns_with_unique_index' => $columns_with_unique_index, - 'edit_view_url' => $edit_view_url, 'columns_list' => $columns_list, 'table_stats' => isset($tablestats) ? $tablestats : null, 'fields' => $fields, diff --git a/libraries/classes/Core.php b/libraries/classes/Core.php index bf5edde41a..621544edf5 100644 --- a/libraries/classes/Core.php +++ b/libraries/classes/Core.php @@ -1317,4 +1317,36 @@ class Core $hmac = hash_hmac('sha256', $sqlQuery, $_SESSION[' HMAC_secret '] . $cfg['blowfish_secret']); return hash_equals($hmac, $signature); } + + /** + * @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; + } + } } diff --git a/libraries/classes/Crypto/Crypto.php b/libraries/classes/Crypto/Crypto.php new file mode 100644 index 0000000000..6449f7dca5 --- /dev/null +++ b/libraries/classes/Crypto/Crypto.php @@ -0,0 +1,155 @@ +<?php + +namespace PhpMyAdmin\Crypto; + +use phpseclib\Crypt\AES; +use phpseclib\Crypt\Random; + +final class Crypto +{ + /** @var bool */ + private $hasRandomBytesSupport; + + /** @var bool */ + private $hasSodiumSupport; + + /** + * @param bool $forceFallback Force the usage of the fallback functions. + */ + public function __construct($forceFallback = false) + { + $this->hasRandomBytesSupport = ! $forceFallback && is_callable('random_bytes'); + $this->hasSodiumSupport = ! $forceFallback + && $this->hasRandomBytesSupport + && is_callable('sodium_crypto_secretbox') + && is_callable('sodium_crypto_secretbox_open') + && defined('SODIUM_CRYPTO_SECRETBOX_NONCEBYTES') + && defined('SODIUM_CRYPTO_SECRETBOX_KEYBYTES'); + } + + /** + * @param string $plaintext + * + * @return string + */ + public function encrypt($plaintext) + { + if ($this->hasSodiumSupport) { + return $this->encryptWithSodium($plaintext); + } + + return $this->encryptWithPhpseclib($plaintext); + } + + /** + * @param string $ciphertext + * + * @return string + */ + public function decrypt($ciphertext) + { + if ($this->hasSodiumSupport) { + return $this->decryptWithSodium($ciphertext); + } + + return $this->decryptWithPhpseclib($ciphertext); + } + + /** + * @return string + */ + private function getEncryptionKey() + { + global $PMA_Config; + + $keyLength = $this->hasSodiumSupport ? SODIUM_CRYPTO_SECRETBOX_KEYBYTES : 32; + + $key = $PMA_Config->get('URLQueryEncryptionSecretKey'); + if (is_string($key) && mb_strlen($key, '8bit') === $keyLength) { + return $key; + } + + $key = isset($_SESSION['URLQueryEncryptionSecretKey']) ? $_SESSION['URLQueryEncryptionSecretKey'] : null; + if (is_string($key) && mb_strlen($key, '8bit') === $keyLength) { + return $key; + } + + $key = $this->hasRandomBytesSupport ? random_bytes($keyLength) : Random::string($keyLength); + $_SESSION['URLQueryEncryptionSecretKey'] = $key; + + return $key; + } + + /** + * @param string $plaintext + * + * @return string + */ + private function encryptWithPhpseclib($plaintext) + { + $key = $this->getEncryptionKey(); + $cipher = new AES(AES::MODE_CBC); + $iv = $this->hasRandomBytesSupport ? random_bytes(16) : Random::string(16); + $cipher->setIV($iv); + $cipher->setKey($key); + $ciphertext = $cipher->encrypt($plaintext); + $hmac = hash_hmac('sha256', $iv . $ciphertext, $key, true); + + return $hmac . $iv . $ciphertext; + } + + /** + * @param string $encrypted + * + * @return string|null + */ + private function decryptWithPhpseclib($encrypted) + { + $key = $this->getEncryptionKey(); + $hmac = mb_substr($encrypted, 0, 32, '8bit'); + $iv = mb_substr($encrypted, 32, 16, '8bit'); + $ciphertext = mb_substr($encrypted, 48, null, '8bit'); + $calculatedHmac = hash_hmac('sha256', $iv . $ciphertext, $key, true); + if (! hash_equals($hmac, $calculatedHmac)) { + return null; + } + + $cipher = new AES(AES::MODE_CBC); + $cipher->setIV($iv); + $cipher->setKey($key); + + return $cipher->decrypt($ciphertext); + } + + /** + * @param string $plaintext + * + * @return string + */ + private function encryptWithSodium($plaintext) + { + $key = $this->getEncryptionKey(); + $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); + $ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key); + + return $nonce . $ciphertext; + } + + /** + * @param string $encrypted + * + * @return string|null + */ + private function decryptWithSodium($encrypted) + { + $key = $this->getEncryptionKey(); + $nonce = mb_substr($encrypted, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); + $ciphertext = mb_substr($encrypted, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); + $decrypted = sodium_crypto_secretbox_open($ciphertext, $nonce, $key); + if ($decrypted === false) { + return null; + } + + return $decrypted; + } +} diff --git a/libraries/classes/Display/Results.php b/libraries/classes/Display/Results.php index 6809c227c9..e371df95c4 100644 --- a/libraries/classes/Display/Results.php +++ b/libraries/classes/Display/Results.php @@ -1736,9 +1736,8 @@ class Results $tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="' . $tmp_txt . '" title="' . $tmp_txt . '" />'; - $tmp_url = 'sql.php' . Url::getCommon($url_params_full_text); - return Util::linkOrButton($tmp_url, $tmp_image); + return Util::linkOrButton('sql.php', $url_params_full_text, $tmp_image); } // end of the '_getFullOrPartialTextButtonOrLink()' function @@ -1869,13 +1868,11 @@ class Results 'session_max_rows' => $session_max_rows, 'is_browse_distinct' => $this->__get('is_browse_distinct'), ); - $single_order_url = 'sql.php' . Url::getCommon($_single_url_params); - $multi_order_url = 'sql.php' . Url::getCommon($_multi_url_params); // Displays the sorting URL // enable sort order swapping for image $order_link = $this->_getSortOrderLink( - $order_img, $fields_meta, $single_order_url, $multi_order_url + $order_img, $fields_meta, $_single_url_params, $_multi_url_params ); $sorted_header_html .= $this->_getDraggableClassForSortableColumns( @@ -2143,10 +2140,10 @@ class Results /** * Get sort order link * - * @param string $order_img the sort order image - * @param array $fields_meta set of field properties - * @param string $order_url the url for sort - * @param string $multi_order_url the url for sort + * @param string $order_img the sort order image + * @param array $fields_meta set of field properties + * @param array $order_url_params the url params for sort + * @param array $multi_order_url_params the url params for sort * * @return string the sort order link * @@ -2155,7 +2152,7 @@ class Results * @see _getTableHeaders() */ private function _getSortOrderLink( - $order_img, $fields_meta, $order_url, $multi_order_url + $order_img, $fields_meta, $order_url_params, $multi_order_url_params ) { $order_link_params = array( 'class' => 'sortlink' @@ -2163,10 +2160,12 @@ class Results $order_link_content = htmlspecialchars($fields_meta->name); $inner_link_content = $order_link_content . $order_img - . '<input type="hidden" value="' . $multi_order_url . '" />'; + . '<input type="hidden" value="sql.php' + . Url::getCommon($multi_order_url_params, '?', false) + . '" />'; return Util::linkOrButton( - $order_url, $inner_link_content, $order_link_params + 'sql.php', $order_url_params, $inner_link_content, $order_link_params ); } // end of the '_getSortOrderLink()' function @@ -2608,7 +2607,7 @@ class Results // 1. Prepares the row // In print view these variable needs to be initialized - $del_url = $del_str = $edit_anchor_class + $del_url = $del_str = $edit_anchor_class = $editCopyUrlParams = $delUrlParams = $edit_str = $js_conf = $copy_url = $copy_str = $edit_url = null; // 1.2 Defines the URLs for the modify/delete link(s) @@ -2643,7 +2642,7 @@ class Results if ($displayParts['edit_lnk'] == self::UPDATE_ROW) { list($edit_url, $copy_url, $edit_str, $copy_str, - $edit_anchor_class) + $edit_anchor_class, $editCopyUrlParams) = $this->_getModifiedLinks( $where_clause, $clause_is_unique, $url_sql_query @@ -2652,7 +2651,7 @@ class Results } // end if (1.2.1) // 1.2.2 Delete/Kill link(s) - list($del_url, $del_str, $js_conf) + list($del_url, $del_str, $js_conf, $delUrlParams) = $this->_getDeleteAndKillLinks( $where_clause, $clause_is_unique, $url_sql_query, $displayParts['del_lnk'], @@ -2668,7 +2667,7 @@ class Results self::POSITION_LEFT, $del_url, $displayParts, $row_no, $where_clause, $where_clause_html, $condition_array, $edit_url, $copy_url, $edit_anchor_class, - $edit_str, $copy_str, $del_str, $js_conf + $edit_str, $copy_str, $del_str, $js_conf, $editCopyUrlParams, $delUrlParams ); } elseif ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) { @@ -2677,7 +2676,7 @@ class Results self::POSITION_NONE, $del_url, $displayParts, $row_no, $where_clause, $where_clause_html, $condition_array, $edit_url, $copy_url, $edit_anchor_class, - $edit_str, $copy_str, $del_str, $js_conf + $edit_str, $copy_str, $del_str, $js_conf, $editCopyUrlParams, $delUrlParams ); } // end if (1.3) @@ -2711,7 +2710,7 @@ class Results self::POSITION_RIGHT, $del_url, $displayParts, $row_no, $where_clause, $where_clause_html, $condition_array, $edit_url, $copy_url, $edit_anchor_class, - $edit_str, $copy_str, $del_str, $js_conf + $edit_str, $copy_str, $del_str, $js_conf, $editCopyUrlParams, $delUrlParams ); } @@ -3327,15 +3326,9 @@ class Results 'goto' => 'sql.php', ); - $edit_url = 'tbl_change.php' - . Url::getCommon( - $_url_params + array('default_action' => 'update') - ); + $edit_url = 'tbl_change.php'; - $copy_url = 'tbl_change.php' - . Url::getCommon( - $_url_params + array('default_action' => 'insert') - ); + $copy_url = 'tbl_change.php'; $edit_str = $this->_getActionLinkContent( 'b_edit', __('Edit') @@ -3350,7 +3343,7 @@ class Results $edit_anchor_class .= ' nonunique'; } - return array($edit_url, $copy_url, $edit_str, $copy_str, $edit_anchor_class); + return array($edit_url, $copy_url, $edit_str, $copy_str, $edit_anchor_class, $_url_params); } // end of the '_getModifiedLinks()' function @@ -3401,7 +3394,7 @@ class Results 'message_to_show' => __('The row has been deleted.'), 'goto' => $lnk_goto, ); - $del_url = 'sql.php' . Url::getCommon($_url_params); + $del_url = 'sql.php'; $js_conf = 'DELETE FROM ' . Sanitize::jsFormat($this->__get('table')) . ' WHERE ' . Sanitize::jsFormat($where_clause, false) @@ -3428,16 +3421,16 @@ class Results 'goto' => $lnk_goto, ); - $del_url = 'sql.php' . Url::getCommon($_url_params); + $del_url = 'sql.php'; $js_conf = $kill; $del_str = Util::getIcon( 'b_drop', __('Kill') ); } else { - $del_url = $del_str = $js_conf = null; + $del_url = $del_str = $js_conf = $_url_params = null; } - return array($del_url, $del_str, $js_conf); + return array($del_url, $del_str, $js_conf, $_url_params); } // end of the '_getDeleteAndKillLinks()' function @@ -3505,6 +3498,8 @@ class Results * @param string $copy_str the label for copy row * @param string $del_str the label for delete row * @param string $js_conf text for the JS confirmation + * @param array $editCopyUrlParams URL parameters + * @param array $delUrlParams URL parameters * * @return string html content * @@ -3515,7 +3510,7 @@ class Results private function _getPlacedLinks( $dir, $del_url, array $displayParts, $row_no, $where_clause, $where_clause_html, array $condition_array, $edit_url, $copy_url, - $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf + $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf, $editCopyUrlParams, $delUrlParams ) { if (! isset($js_conf)) { @@ -3526,7 +3521,7 @@ class Results $dir, $del_url, $displayParts, $row_no, $where_clause, $where_clause_html, $condition_array, $edit_url, $copy_url, $edit_anchor_class, - $edit_str, $copy_str, $del_str, $js_conf + $edit_str, $copy_str, $del_str, $js_conf, $editCopyUrlParams, $delUrlParams ); } // end of the '_getPlacedLinks()' function @@ -4804,8 +4799,8 @@ class Results /** * Generates HTML to display the Create view in span tag * - * @param array $analyzed_sql_results analyzed sql results - * @param string $url_query String with URL Parameters + * @param array $analyzed_sql_results analyzed sql results + * @param array $url_query URL Parameters * * @return string * @@ -4813,14 +4808,15 @@ class Results * * @see _getResultsOperations() */ - private function _getLinkForCreateView(array $analyzed_sql_results, $url_query) + private function _getLinkForCreateView(array $analyzed_sql_results, $urlParams) { $results_operations_html = ''; if (empty($analyzed_sql_results['procedure'])) { $results_operations_html .= '<span>' . Util::linkOrButton( - 'view_create.php' . $url_query, + 'view_create.php', + $urlParams, Util::getIcon( 'b_view_add', __('Create view'), true ), @@ -4867,6 +4863,7 @@ class Results { $html = Util::linkOrButton( '#', + null, Util::getIcon( 'b_insrow', __('Copy to clipboard'), true ), @@ -4887,6 +4884,7 @@ class Results { $html = Util::linkOrButton( '#', + null, Util::getIcon( 'b_print', __('Print'), true ), @@ -4927,7 +4925,6 @@ class Results 'printview' => '1', 'sql_query' => $this->__get('sql_query'), ); - $url_query = Url::getCommon($_url_params); if (!$header_shown) { $results_operations_html .= $header; @@ -4937,7 +4934,7 @@ class Results // show only view and not other options if ($only_view) { $results_operations_html .= $this->_getLinkForCreateView( - $analyzed_sql_results, $url_query + $analyzed_sql_results, $_url_params ); if ($header_shown) { @@ -4992,7 +4989,8 @@ class Results } $results_operations_html .= Util::linkOrButton( - 'tbl_export.php' . Url::getCommon($_url_params), + 'tbl_export.php', + $_url_params, Util::getIcon( 'b_tblexport', __('Export'), true ) @@ -5001,7 +4999,8 @@ class Results // prepare chart $results_operations_html .= Util::linkOrButton( - 'tbl_chart.php' . Url::getCommon($_url_params), + 'tbl_chart.php', + $_url_params, Util::getIcon( 'b_chart', __('Display chart'), true ) @@ -5021,8 +5020,8 @@ class Results if ($geometry_found) { $results_operations_html .= Util::linkOrButton( - 'tbl_gis_visualization.php' - . Url::getCommon($_url_params), + 'tbl_gis_visualization.php', + $_url_params, Util::getIcon( 'b_globe', __('Visualize GIS data'), @@ -5047,7 +5046,7 @@ class Results } $results_operations_html .= $this->_getLinkForCreateView( - $analyzed_sql_results, $url_query + $analyzed_sql_results, $_url_params ); if ($header_shown) { @@ -5367,7 +5366,7 @@ class Results $tag_params['class'] = 'ajax'; } $result .= Util::linkOrButton( - 'sql.php' . Url::getCommon($_url_params), + 'sql.php', $_url_params, $displayedData, $tag_params ); } @@ -5443,6 +5442,7 @@ class Results * Prepares an Edit link * * @param string $edit_url edit url + * @param array $urlParams URL parameters * @param string $class css classes for td element * @param string $edit_str text for the edit link * @param string $where_clause where clause @@ -5455,7 +5455,7 @@ class Results * @see _getTableBody(), _getCheckboxAndLinks() */ private function _getEditLink( - $edit_url, $class, $edit_str, $where_clause, $where_clause_html + $edit_url, $urlParams, $class, $edit_str, $where_clause, $where_clause_html ) { $ret = ''; @@ -5463,7 +5463,7 @@ class Results $ret .= '<td class="' . $class . ' center print_ignore" ' . ' ><span class="nowrap">' - . Util::linkOrButton($edit_url, $edit_str); + . Util::linkOrButton($edit_url, $urlParams, $edit_str); /* * Where clause for selecting this row uniquely is provided as * a hidden input. Used by jQuery scripts for handling grid editing @@ -5484,6 +5484,7 @@ class Results * Prepares an Copy link * * @param string $copy_url copy url + * @param array $urlParams URL parameters * @param string $copy_str text for the copy link * @param string $where_clause where clause * @param string $where_clause_html url encoded where clause @@ -5496,7 +5497,7 @@ class Results * @see _getTableBody(), _getCheckboxAndLinks() */ private function _getCopyLink( - $copy_url, $copy_str, $where_clause, $where_clause_html, $class + $copy_url, $urlParams, $copy_str, $where_clause, $where_clause_html, $class ) { $ret = ''; @@ -5508,7 +5509,7 @@ class Results } $ret .= 'center print_ignore" ' . ' ><span class="nowrap">' - . Util::linkOrButton($copy_url, $copy_str); + . Util::linkOrButton($copy_url, $urlParams, $copy_str); /* * Where clause for selecting this row uniquely is provided as @@ -5529,10 +5530,11 @@ class Results /** * Prepares a Delete link * - * @param string $del_url delete url - * @param string $del_str text for the delete link - * @param string $js_conf text for the JS confirmation - * @param string $class css classes for the td element + * @param string $del_url delete url + * @param array $delUrlParams URL parameters + * @param string $del_str text for the delete link + * @param string $js_conf text for the JS confirmation + * @param string $class css classes for the td element * * @return string the generated HTML * @@ -5540,7 +5542,7 @@ class Results * * @see _getTableBody(), _getCheckboxAndLinks() */ - private function _getDeleteLink($del_url, $del_str, $js_conf, $class) + private function _getDeleteLink($del_url, $delUrlParams, $del_str, $js_conf, $class) { $ret = ''; @@ -5556,6 +5558,7 @@ class Results $ret .= 'center print_ignore" ' . ' >' . Util::linkOrButton( $del_url, + $delUrlParams, $del_str, array('class' => 'delete_row requireConfirm' . $ajax) ) @@ -5586,6 +5589,8 @@ class Results * @param string $copy_str text for the copy link * @param string $del_str text for the delete link * @param string $js_conf text for the JS confirmation + * @param array $editCopyUrlParams URL parameters + * @param array $delUrlParams URL parameters * * @return string the generated HTML * @@ -5596,49 +5601,51 @@ class Results private function _getCheckboxAndLinks( $position, $del_url, array $displayParts, $row_no, $where_clause, $where_clause_html, array $condition_array, - $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf + $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf, $editCopyUrlParams, $delUrlParams ) { $ret = ''; + $editUrlParams = $editCopyUrlParams + array('default_action' => 'update'); + $copyUrlParams = $editCopyUrlParams + array('default_action' => 'insert'); if ($position == self::POSITION_LEFT) { $ret .= $this->_getCheckboxForMultiRowSubmissions( - $del_url, $displayParts, $row_no, $where_clause_html, + $del_url . Url::getCommon($delUrlParams), $displayParts, $row_no, $where_clause_html, $condition_array, '_left', '' ); $ret .= $this->_getEditLink( - $edit_url, $class, $edit_str, $where_clause, $where_clause_html + $edit_url, $editUrlParams, $class, $edit_str, $where_clause, $where_clause_html ); $ret .= $this->_getCopyLink( - $copy_url, $copy_str, $where_clause, $where_clause_html, '' + $copy_url, $copyUrlParams, $copy_str, $where_clause, $where_clause_html, '' ); - $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, ''); + $ret .= $this->_getDeleteLink($del_url, $delUrlParams, $del_str, $js_conf, ''); } elseif ($position == self::POSITION_RIGHT) { - $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, ''); + $ret .= $this->_getDeleteLink($del_url, $delUrlParams, $del_str, $js_conf, ''); $ret .= $this->_getCopyLink( - $copy_url, $copy_str, $where_clause, $where_clause_html, '' + $copy_url, $copyUrlParams, $copy_str, $where_clause, $where_clause_html, '' ); $ret .= $this->_getEditLink( - $edit_url, $class, $edit_str, $where_clause, $where_clause_html + $edit_url, $editUrlParams, $class, $edit_str, $where_clause, $where_clause_html ); $ret .= $this->_getCheckboxForMultiRowSubmissions( - $del_url, $displayParts, $row_no, $where_clause_html, + $del_url . Url::getCommon($delUrlParams), $displayParts, $row_no, $where_clause_html, $condition_array, '_right', '' ); } else { // $position == self::POSITION_NONE $ret .= $this->_getCheckboxForMultiRowSubmissions( - $del_url, $displayParts, $row_no, $where_clause_html, + $del_url . Url::getCommon($delUrlParams), $displayParts, $row_no, $where_clause_html, $condition_array, '_left', '' ); } diff --git a/libraries/classes/ErrorReport.php b/libraries/classes/ErrorReport.php index 6a3e65a230..3abef4815d 100644 --- a/libraries/classes/ErrorReport.php +++ b/libraries/classes/ErrorReport.php @@ -197,6 +197,7 @@ class ErrorReport unset($queryArray["table"]); unset($queryArray["token"]); unset($queryArray["server"]); + unset($queryArray["eq"]); $query = http_build_query($queryArray); } else { $query = ''; diff --git a/libraries/classes/Export.php b/libraries/classes/Export.php index e4d028b224..affcc00af2 100644 --- a/libraries/classes/Export.php +++ b/libraries/classes/Export.php @@ -479,14 +479,14 @@ class Export */ $back_button = '<p id="export_back_button">[ <a href="'; if ($export_type == 'server') { - $back_button .= 'server_export.php" data-post="' . Url::getCommon([], ''); + $back_button .= 'server_export.php" data-post="' . Url::getCommon([], '', false); } elseif ($export_type == 'database') { - $back_button .= 'db_export.php" data-post="' . Url::getCommon(array('db' => $db), ''); + $back_button .= 'db_export.php" data-post="' . Url::getCommon(array('db' => $db), '', false); } else { $back_button .= 'tbl_export.php" data-post="' . Url::getCommon( array( 'db' => $db, 'table' => $table - ), '' + ), '', false ); } diff --git a/libraries/classes/Index.php b/libraries/classes/Index.php index 4c6aebde0d..d1f12a032c 100644 --- a/libraries/classes/Index.php +++ b/libraries/classes/Index.php @@ -741,7 +741,7 @@ class Index $r .= '" ' . $row_span . '>' . ' <a class="'; $r .= 'ajax'; - $r .= '" href="tbl_indexes.php" data-post="' . Url::getCommon($this_params, '') + $r .= '" href="tbl_indexes.php" data-post="' . Url::getCommon($this_params, '', false) . '">' . Util::getIcon('b_edit', __('Edit')) . '</a>' . '</td>' . "\n"; $this_params = $GLOBALS['url_params']; @@ -766,7 +766,8 @@ class Index $r .= '<input type="hidden" class="drop_primary_key_index_msg"' . ' value="' . $js_msg . '" />'; $r .= Util::linkOrButton( - 'sql.php' . Url::getCommon($this_params), + 'sql.php', + $this_params, Util::getIcon('b_drop', __('Drop')), array('class' => 'drop_primary_key_index_anchor ajax') ); diff --git a/libraries/classes/InsertEdit.php b/libraries/classes/InsertEdit.php index ad692f0509..99201cde7e 100644 --- a/libraries/classes/InsertEdit.php +++ b/libraries/classes/InsertEdit.php @@ -280,12 +280,12 @@ class InsertEdit if (! $is_show) { return ' : <a href="tbl_change.php" data-post="' - . Url::getCommon($this_url_params, '') . '">' + . Url::getCommon($this_url_params, '', false) . '">' . $this->showTypeOrFunctionLabel($which) . '</a>'; } return '<th><a href="tbl_change.php" data-post="' - . Url::getCommon($this_url_params, '') + . Url::getCommon($this_url_params, '', false) . '" title="' . __('Hide') . '">' . $this->showTypeOrFunctionLabel($which) . '</a></th>'; @@ -857,7 +857,7 @@ class InsertEdit 'rownumber' => $rownumber, 'data' => $data ), - '' + '', false ) . '">' . str_replace("'", "\'", $titles['Browse']) . '</a>'; return $html_output; @@ -1704,6 +1704,7 @@ class InsertEdit return '<span class="open_gis_editor">' . Util::linkOrButton( '#', + null, $edit_str, array(), '_blank' diff --git a/libraries/classes/Navigation/Navigation.php b/libraries/classes/Navigation/Navigation.php index 486bd2f74c..145167a45a 100644 --- a/libraries/classes/Navigation/Navigation.php +++ b/libraries/classes/Navigation/Navigation.php @@ -231,7 +231,7 @@ class Navigation $html .= '<tr>'; $html .= '<td>' . htmlspecialchars($hiddenItem) . '</td>'; $html .= '<td style="width:80px"><a href="navigation.php" data-post="' - . Url::getCommon($params, '') . '"' + . Url::getCommon($params, '', false) . '"' . ' class="unhideNavItem ajax">' . Util::getIcon('show', __('Show')) . '</a></td>'; diff --git a/libraries/classes/Navigation/NavigationTree.php b/libraries/classes/Navigation/NavigationTree.php index 78659a4b7f..1bc35947de 100644 --- a/libraries/classes/Navigation/NavigationTree.php +++ b/libraries/classes/Navigation/NavigationTree.php @@ -102,13 +102,13 @@ class NavigationTree $this->_pos = $this->_getNavigationDbPos(); } // Get the active node - if (isset($_REQUEST['aPath'])) { - $this->_aPath[0] = $this->_parsePath($_REQUEST['aPath']); - $this->_pos2_name[0] = $_REQUEST['pos2_name']; - $this->_pos2_value[0] = $_REQUEST['pos2_value']; - if (isset($_REQUEST['pos3_name'])) { - $this->_pos3_name[0] = $_REQUEST['pos3_name']; - $this->_pos3_value[0] = $_REQUEST['pos3_value']; + if (isset($_POST['aPath'])) { + $this->_aPath[0] = $this->_parsePath($_POST['aPath']); + $this->_pos2_name[0] = $_POST['pos2_name']; + $this->_pos2_value[0] = $_POST['pos2_value']; + if (isset($_POST['pos3_name'])) { + $this->_pos3_name[0] = $_POST['pos3_name']; + $this->_pos3_value[0] = $_POST['pos3_value']; } } else { if (isset($_POST['n0_aPath'])) { @@ -129,8 +129,8 @@ class NavigationTree } } } - if (isset($_REQUEST['vPath'])) { - $this->_vPath[0] = $this->_parsePath($_REQUEST['vPath']); + if (isset($_POST['vPath'])) { + $this->_vPath[0] = $this->_parsePath($_POST['vPath']); } else { if (isset($_POST['n0_vPath'])) { $count = 0; @@ -142,11 +142,11 @@ class NavigationTree } } } - if (isset($_REQUEST['searchClause'])) { - $this->_searchClause = $_REQUEST['searchClause']; + if (isset($_POST['searchClause'])) { + $this->_searchClause = $_POST['searchClause']; } - if (isset($_REQUEST['searchClause2'])) { - $this->_searchClause2 = $_REQUEST['searchClause2']; + if (isset($_POST['searchClause2'])) { + $this->_searchClause2 = $_POST['searchClause2']; } // Initialise the tree by creating a root node $node = NodeFactory::getInstance('NodeDatabaseContainer', 'root'); @@ -1156,7 +1156,7 @@ class NavigationTree } foreach ($icons as $key => $icon) { - $link = vsprintf($iconLinks[$key], $args); + $link = $this->encryptQueryParams(vsprintf($iconLinks[$key], $args)); if ($linkClass != '') { $retval .= "<a class='$linkClass' href='$link'>"; $retval .= "{$icon}</a>"; @@ -1174,7 +1174,7 @@ class NavigationTree foreach ($node->parents(true) as $parent) {; $args[] = urlencode($parent->real_name); } - $link = vsprintf($node->links['text'], $args); + $link = $this->encryptQueryParams(vsprintf($node->links['text'], $args)); $title = isset($node->links['title']) ? $node->links['title'] : ''; if ($node->type == Node::CONTAINER) { $retval .= " <a class='hover_show_full' href='$link'>"; @@ -1557,4 +1557,23 @@ class NavigationTree return $retval; } + + /** + * @param string $link + * + * @return string + */ + private function encryptQueryParams($link) + { + global $PMA_Config; + + if (! $PMA_Config->get('URLQueryEncryption')) { + return $link; + } + + $url = parse_url($link); + parse_str(htmlspecialchars_decode($url['query']), $query); + + return $url['path'] . '?' . htmlspecialchars(Url::buildHttpQuery($query)); + } } diff --git a/libraries/classes/Navigation/Nodes/NodeDatabase.php b/libraries/classes/Navigation/Nodes/NodeDatabase.php index d9828a02f6..0844f56380 100644 --- a/libraries/classes/Navigation/Nodes/NodeDatabase.php +++ b/libraries/classes/Navigation/Nodes/NodeDatabase.php @@ -678,7 +678,7 @@ class NodeDatabase extends Node ); $ret = '<span class="dbItemControls">' . '<a href="navigation.php" data-post="' - . Url::getCommon($params, '') . '"' + . Url::getCommon($params, '', false) . '"' . ' class="showUnhide ajax">' . Util::getImage( 'show', diff --git a/libraries/classes/Navigation/Nodes/NodeDatabaseChild.php b/libraries/classes/Navigation/Nodes/NodeDatabaseChild.php index ecae8fab2d..e562af0b74 100644 --- a/libraries/classes/Navigation/Nodes/NodeDatabaseChild.php +++ b/libraries/classes/Navigation/Nodes/NodeDatabaseChild.php @@ -49,7 +49,7 @@ abstract class NodeDatabaseChild extends Node $ret = '<span class="navItemControls">' . '<a href="navigation.php" data-post="' - . Url::getCommon($params, '') . '"' + . Url::getCommon($params, '', false) . '"' . ' class="hideNavItem ajax">' . Util::getImage('hide', __('Hide')) . '</a></span>'; diff --git a/libraries/classes/Operations.php b/libraries/classes/Operations.php index 620031f7ac..df94c04c71 100644 --- a/libraries/classes/Operations.php +++ b/libraries/classes/Operations.php @@ -1507,7 +1507,8 @@ class Operations { return '<li>' . Util::linkOrButton( - 'sql.php' . Url::getCommon(array_merge($url_params, $params)), + 'sql.php', + array_merge($url_params, $params), $action_message, ['class' => 'maintain_action ajax'] ) @@ -1567,7 +1568,8 @@ class Operations public function getDeleteDataOrTablelink(array $url_params, $syntax, $link, $htmlId) { return '<li>' . Util::linkOrButton( - 'sql.php' . Url::getCommon($url_params), + 'sql.php', + $url_params, $link, array('id' => $htmlId, 'class' => 'ajax') ) diff --git a/libraries/classes/ReplicationGui.php b/libraries/classes/ReplicationGui.php index f9e3534df8..e1f0e1e5ff 100644 --- a/libraries/classes/ReplicationGui.php +++ b/libraries/classes/ReplicationGui.php @@ -73,7 +73,7 @@ class ReplicationGui $_url_params['repl_clear_scr'] = true; $html .= ' <li><a href="server_replication.php" data-post="'; - $html .= Url::getCommon($_url_params, '') + $html .= Url::getCommon($_url_params, '', false) . '" id="master_addslaveuser_href">'; $html .= __('Add slave replication user') . '</a></li>'; } @@ -188,7 +188,7 @@ class ReplicationGui } $_url_params['sr_slave_control_parm'] = 'IO_THREAD'; - $slave_control_io_link = Url::getCommon($_url_params, ''); + $slave_control_io_link = Url::getCommon($_url_params, '', false); if ($server_slave_replication[0]['Slave_SQL_Running'] == 'No') { $_url_params['sr_slave_action'] = 'start'; @@ -197,7 +197,7 @@ class ReplicationGui } $_url_params['sr_slave_control_parm'] = 'SQL_THREAD'; - $slave_control_sql_link = Url::getCommon($_url_params, ''); + $slave_control_sql_link = Url::getCommon($_url_params, '', false); if ($server_slave_replication[0]['Slave_IO_Running'] == 'No' || $server_slave_replication[0]['Slave_SQL_Running'] == 'No' @@ -208,15 +208,15 @@ class ReplicationGui } $_url_params['sr_slave_control_parm'] = null; - $slave_control_full_link = Url::getCommon($_url_params, ''); + $slave_control_full_link = Url::getCommon($_url_params, '', false); $_url_params['sr_slave_action'] = 'reset'; - $slave_control_reset_link = Url::getCommon($_url_params, ''); + $slave_control_reset_link = Url::getCommon($_url_params, '', false); $_url_params = $GLOBALS['url_params']; $_url_params['sr_take_action'] = true; $_url_params['sr_slave_skip_error'] = true; - $slave_skip_error_link = Url::getCommon($_url_params, ''); + $slave_skip_error_link = Url::getCommon($_url_params, '', false); if ($server_slave_replication[0]['Slave_SQL_Running'] == 'No') { $html .= Message::error( @@ -233,7 +233,7 @@ class ReplicationGui $_url_params['sl_configure'] = true; $_url_params['repl_clear_scr'] = true; - $reconfiguremaster_link = Url::getCommon($_url_params, ''); + $reconfiguremaster_link = Url::getCommon($_url_params, '', false); $html .= __( 'Server is configured as slave in a replication process. Would you ' . @@ -293,7 +293,7 @@ class ReplicationGui 'This server is not configured as slave in a replication process. ' . 'Would you like to %sconfigure%s it?' ), - '<a href="server_replication.php" data-post="' . Url::getCommon($_url_params, '') . '">', + '<a href="server_replication.php" data-post="' . Url::getCommon($_url_params, '', false) . '">', '</a>' ); } @@ -355,7 +355,7 @@ class ReplicationGui 'This server is not configured as master in a replication process. ' . 'Would you like to %sconfigure%s it?' ), - '<a href="server_replication.php" data-post="' . Url::getCommon($_url_params, '') . '">', + '<a href="server_replication.php" data-post="' . Url::getCommon($_url_params, '', false) . '">', '</a>' ); $html .= '</fieldset>'; diff --git a/libraries/classes/Rte/RteList.php b/libraries/classes/Rte/RteList.php index fedf3278b6..677a5c7831 100644 --- a/libraries/classes/Rte/RteList.php +++ b/libraries/classes/Rte/RteList.php @@ -191,7 +191,7 @@ class RteList */ public static function getRoutineRow(array $routine, $rowclass = '') { - global $url_query, $db, $titles; + global $url_query, $url_params, $db, $titles; $sql_drop = sprintf( 'DROP %s IF EXISTS %s', @@ -316,7 +316,11 @@ class RteList $retval .= " </td>\n"; $retval .= " <td>\n"; $retval .= Util::linkOrButton( - 'sql.php' . $url_query . '&sql_query=' . urlencode($sql_drop) . '&goto=db_routines.php' . urlencode("?db={$db}"), + 'sql.php', + array_merge( + $url_params, + ['sql_query' => $sql_drop, 'goto' => 'db_routines.php' . Url::getCommon(['db' => $db])] + ), $titles['Drop'], ['class' => 'ajax drop_anchor'] ); @@ -343,7 +347,7 @@ class RteList */ public static function getTriggerRow(array $trigger, $rowclass = '') { - global $url_query, $db, $table, $titles; + global $url_query, $url_params, $db, $table, $titles; $retval = " <tr class='$rowclass'>\n"; $retval .= " <td>\n"; @@ -360,8 +364,8 @@ class RteList $retval .= " </td>\n"; if (empty($table)) { $retval .= " <td>\n"; - $retval .= "<a href='db_triggers.php{$url_query}" - . "&table=" . urlencode($trigger['table']) . "'>" + $retval .= "<a href='db_triggers.php" + . Url::getCommon(array_merge($url_params, ['table' => $trigger['table']])) . "'>" . htmlspecialchars($trigger['table']) . "</a>"; $retval .= " </td>\n"; } @@ -390,7 +394,11 @@ class RteList $retval .= " <td>\n"; if (Util::currentUserHasPrivilege('TRIGGER', $db)) { $retval .= Util::linkOrButton( - 'sql.php' . $url_query . '&sql_query=' . urlencode($trigger['drop']) . '&goto=db_triggers.php' . urlencode("?db={$db}"), + 'sql.php', + array_merge( + $url_params, + ['sql_query' => $trigger['drop'], 'goto' => 'db_triggers.php' . Url::getCommon(['db' => $db])] + ), $titles['Drop'], ['class' => 'ajax drop_anchor'] ); @@ -419,7 +427,7 @@ class RteList */ public static function getEventRow(array $event, $rowclass = '') { - global $url_query, $db, $titles; + global $url_query, $url_params, $db, $titles; $sql_drop = sprintf( 'DROP EVENT IF EXISTS %s', @@ -468,7 +476,11 @@ class RteList $retval .= " <td>\n"; if (Util::currentUserHasPrivilege('EVENT', $db)) { $retval .= Util::linkOrButton( - 'sql.php' . $url_query . '&sql_query=' . urlencode($sql_drop) . '&goto=db_events.php' . urlencode("?db={$db}"), + 'sql.php', + array_merge( + $url_params, + ['sql_query' => $sql_drop, 'goto' => 'db_events.php' . Url::getCommon(['db' => $db])] + ), $titles['Drop'], ['class' => 'ajax drop_anchor'] ); diff --git a/libraries/classes/Server/Privileges.php b/libraries/classes/Server/Privileges.php index a62d111606..eb34b250b3 100644 --- a/libraries/classes/Server/Privileges.php +++ b/libraries/classes/Server/Privileges.php @@ -2844,7 +2844,7 @@ class Privileges $html .= ' href="server_privileges.php'; if ($linktype == 'revoke') { - $html .= '" data-post="' . Url::getCommon($params, ''); + $html .= '" data-post="' . Url::getCommon($params, '', false); } else { $html .= Url::getCommon($params); } diff --git a/libraries/classes/Server/Status/Processes.php b/libraries/classes/Server/Status/Processes.php index 336b28b6ee..df888b1973 100644 --- a/libraries/classes/Server/Status/Processes.php +++ b/libraries/classes/Server/Status/Processes.php @@ -141,7 +141,7 @@ class Processes } $retval .= '<th>'; - $columnUrl = Url::getCommon($column); + $columnUrl = Url::getCommon($column, '', false); $retval .= '<a href="server_status_processes.php" data-post="' . $columnUrl . '" class="sortlink">'; $retval .= $column['column_name']; @@ -179,7 +179,7 @@ class Processes if (isset($_POST['sort_order'])) { $url_params['sort_order'] = $_POST['sort_order']; } - $retval .= '<a href="server_status_processes.php" data-post="' . Url::getCommon($url_params, '') . '" >'; + $retval .= '<a href="server_status_processes.php" data-post="' . Url::getCommon($url_params, '', false) . '" >'; if ($show_full_sql) { $retval .= Util::getImage('s_partialtext', __('Truncate Shown Queries'), ['class' => 'icon_fulltext']); @@ -275,7 +275,7 @@ class Processes $retval = '<tr>'; $retval .= '<td><a class="ajax kill_process" href="server_status_processes.php"' - . ' data-post="' . Url::getCommon(['kill' => $process['Id']], '') . '">' + . ' data-post="' . Url::getCommon(['kill' => $process['Id']], '', false) . '">' . __('Kill') . '</a></td>'; $retval .= '<td class="value">' . $process['Id'] . '</td>'; $retval .= '<td>' . htmlspecialchars($process['User']) . '</td>'; diff --git a/libraries/classes/Server/UserGroups.php b/libraries/classes/Server/UserGroups.php index 337cd9a903..35ecac2443 100644 --- a/libraries/classes/Server/UserGroups.php +++ b/libraries/classes/Server/UserGroups.php @@ -115,7 +115,7 @@ class UserGroups array( 'viewUsers' => 1, 'userGroup' => $groupName ), - '' + '', false ) . '">' . Util::getIcon('b_usrlist', __('View users')) @@ -126,7 +126,7 @@ class UserGroups array( 'editUserGroup' => 1, 'userGroup' => $groupName ), - '' + '', false ) . '">' . Util::getIcon('b_edit', __('Edit')) . '</a>'; @@ -137,7 +137,7 @@ class UserGroups array( 'deleteUserGroup' => 1, 'userGroup' => $groupName ), - '' + '', false ) . '">' . Util::getIcon('b_drop', __('Delete')) . '</a>'; diff --git a/libraries/classes/Sql.php b/libraries/classes/Sql.php index f48c85f45b..e7e6619d85 100644 --- a/libraries/classes/Sql.php +++ b/libraries/classes/Sql.php @@ -227,7 +227,7 @@ class Sql . htmlspecialchars($_POST['curr_value']) . '</span>' . '<a href="browse_foreigners.php" data-post="' - . Url::getCommon($_url_params, '') . '"' + . Url::getCommon($_url_params, '', false) . '"' . 'class="ajax browse_foreign" ' . '>' . __('Browse foreign values') . '</a>'; diff --git a/libraries/classes/Tracking.php b/libraries/classes/Tracking.php index 82c8b25aac..87c8d04ec7 100644 --- a/libraries/classes/Tracking.php +++ b/libraries/classes/Tracking.php @@ -199,20 +199,20 @@ class Tracking $html .= Url::getCommon($url_params + [ 'version' => $version['version'], 'submit_delete_version' => true, - ], ''); + ], '', false); $html .= '">' . $delete . '</a></td>'; $html .= '<td><a href="tbl_tracking.php" data-post="'; $html .= Url::getCommon($url_params + [ 'report' => 'true', 'version' => $version['version'], - ], ''); + ], '', false); $html .= '">' . $report . '</a>'; $html .= ' '; $html .= '<a href="tbl_tracking.php" data-post="'; $html .= Url::getCommon($url_params + [ 'snapshot' => 'true', 'version' => $version['version'], - ], ''); + ], '', false); $html .= '">' . $structure . '</a>'; $html .= '</td>'; $html .= '</tr>'; diff --git a/libraries/classes/Url.php b/libraries/classes/Url.php index f527819fbb..9ff516458f 100644 --- a/libraries/classes/Url.php +++ b/libraries/classes/Url.php @@ -7,6 +7,8 @@ */ namespace PhpMyAdmin; +use PhpMyAdmin\Crypto\Crypto; + /** * Static methods for URL/hidden inputs generating * @@ -159,14 +161,15 @@ class Url * * @param mixed $params optional, Contains an associative array with url params * @param string $divider optional character to use instead of '?' + * @param bool $encrypt whether to encrypt URL params * * @return string string with URL parameters * @access public */ - public static function getCommon($params = array(), $divider = '?') + public static function getCommon($params = array(), $divider = '?', $encrypt = true) { return htmlspecialchars( - Url::getCommonRaw($params, $divider) + Url::getCommonRaw($params, $divider, $encrypt) ); } @@ -195,15 +198,15 @@ class Url * * @param mixed $params optional, Contains an associative array with url params * @param string $divider optional character to use instead of '?' + * @param bool $encrypt whether to encrypt URL params * * @return string string with URL parameters * @access public */ - public static function getCommonRaw($params = array(), $divider = '?') + public static function getCommonRaw($params = array(), $divider = '?', $encrypt = true) { /** @var Config $PMA_Config */ global $PMA_Config; - $separator = Url::getArgSeparator(); // avoid overwriting when creating navi panel links to servers if (isset($GLOBALS['server']) @@ -218,7 +221,7 @@ class Url $params['lang'] = $GLOBALS['lang']; } - $query = http_build_query($params, null, $separator); + $query = self::buildHttpQuery($params, $encrypt); if ($divider != '?' || strlen($query) > 0) { return $divider . $query; @@ -228,6 +231,81 @@ class Url } /** + * @param array<string, mixed> $params + * @param bool $encrypt whether to encrypt URL params + * + * @return string + */ + public static function buildHttpQuery($params, $encrypt = true) + { + global $PMA_Config; + + $separator = self::getArgSeparator(); + + if (! $encrypt || ! $PMA_Config->get('URLQueryEncryption')) { + return http_build_query($params, null, $separator); + } + + $data = $params; + $keys = [ + 'db', + 'table', + 'field', + 'sql_query', + 'sql_signature', + 'where_clause', + 'goto', + 'back', + 'message_to_show', + 'username', + 'hostname', + 'dbname', + 'tablename', + 'checkprivsdb', + 'checkprivstable', + ]; + $paramsToEncrypt = []; + foreach ($params as $paramKey => $paramValue) { + if (! in_array($paramKey, $keys)) { + continue; + } + + $paramsToEncrypt[$paramKey] = $paramValue; + unset($data[$paramKey]); + } + + if ($paramsToEncrypt !== []) { + $data['eq'] = self::encryptQuery(json_encode($paramsToEncrypt)); + } + + return http_build_query($data, null, $separator); + } + + /** + * @param string $query + * + * @return string + */ + public static function encryptQuery($query) + { + $crypto = new Crypto(); + + return strtr(base64_encode($crypto->encrypt($query)), '+/', '-_'); + } + + /** + * @param string $query + * + * @return string|null + */ + public static function decryptQuery($query) + { + $crypto = new Crypto(); + + return $crypto->decrypt(base64_decode(strtr($query, '-_', '+/'))); + } + + /** * Returns url separator * * extracted from arg_separator.input as set in php.ini diff --git a/libraries/classes/Util.php b/libraries/classes/Util.php index 50945d6b5d..b624c6962c 100644 --- a/libraries/classes/Util.php +++ b/libraries/classes/Util.php @@ -1039,7 +1039,8 @@ class Util $explain_params['sql_query'] = 'EXPLAIN ' . $sql_query; $explain_link = ' [ ' . self::linkOrButton( - 'import.php' . Url::getCommon($explain_params), + 'import.php', + $explain_params, __('Explain SQL') ) . ' ]'; } elseif (preg_match( @@ -1050,7 +1051,8 @@ class Util = mb_substr($sql_query, 8); $explain_link = ' [ ' . self::linkOrButton( - 'import.php' . Url::getCommon($explain_params), + 'import.php', + $explain_params, __('Skip Explain SQL') ) . ']'; $url = 'https://mariadb.org/explain_analyzer/analyze/' @@ -1059,6 +1061,7 @@ class Util $explain_link .= ' [' . self::linkOrButton( htmlspecialchars('url.php?url=' . urlencode($url)), + null, sprintf(__('Analyze Explain at %s'), 'mariadb.org'), array(), '_blank' @@ -1074,9 +1077,8 @@ class Util if (! empty($cfg['SQLQuery']['Edit']) && empty($GLOBALS['show_as_php']) ) { - $edit_link .= Url::getCommon($url_params); $edit_link = ' [ ' - . self::linkOrButton($edit_link, __('Edit')) + . self::linkOrButton($edit_link, $url_params, __('Edit')) . ' ]'; } else { $edit_link = ''; @@ -1089,14 +1091,16 @@ class Util if (! empty($GLOBALS['show_as_php'])) { $php_link = ' [ ' . self::linkOrButton( - 'import.php' . Url::getCommon($url_params), + 'import.php', + $url_params, __('Without PHP code') ) . ' ]'; $php_link .= ' [ ' . self::linkOrButton( - 'import.php' . Url::getCommon($url_params), + 'import.php', + $url_params, __('Submit query') ) . ' ]'; @@ -1105,7 +1109,8 @@ class Util $php_params['show_as_php'] = 1; $php_link = ' [ ' . self::linkOrButton( - 'import.php' . Url::getCommon($php_params), + 'import.php', + $php_params, __('Create PHP code') ) . ' ]'; @@ -1121,7 +1126,7 @@ class Util ) { $refresh_link = 'import.php' . Url::getCommon($url_params); $refresh_link = ' [ ' - . self::linkOrButton($refresh_link, __('Refresh')) . ']'; + . self::linkOrButton('import.php', $url_params, __('Refresh')) . ']'; } else { $refresh_link = ''; } //refresh @@ -1163,6 +1168,7 @@ class Util $inline_edit_link = ' [' . self::linkOrButton( '#', + null, _pgettext('Inline edit query', 'Edit inline'), array('class' => 'inline_edit_sql') ) @@ -1707,17 +1713,22 @@ class Util * - URL components are over Suhosin limits * - There is SQL query in the parameters * - * @param string $url the URL - * @param string $message the link message - * @param mixed $tag_params string: js confirmation; array: additional tag - * params (f.e. style="") - * @param string $target target + * @param string $urlPath the URL + * @param array|null $urlParams URL parameters + * @param string $message the link message + * @param mixed $tag_params string: js confirmation; array: additional tag params (f.e. style="") + * @param string $target target * * @return string the results to be echoed or saved in an array */ public static function linkOrButton( - $url, $message, $tag_params = array(), $target = '' + $urlPath, $urlParams, $message, $tag_params = array(), $target = '' ) { + $url = $urlPath; + if (is_array($urlParams)) { + $url = $urlPath . Url::getCommon($urlParams, '?', false); + } + $url_length = strlen($url); if (! is_array($tag_params)) { @@ -1776,7 +1787,11 @@ class Util ) { $url .= '?' . explode('&', $parts[1], 2)[0]; } - + } else { + $url = $urlPath; + if (is_array($urlParams)) { + $url = $urlPath . Url::getCommon($urlParams); + } } foreach ($tag_params as $par_name => $par_value) { @@ -4737,9 +4752,7 @@ class Util $urlParams['tbl_group'] = $_REQUEST['tbl_group']; } - $url = 'db_structure.php' . Url::getCommon($urlParams); - - return self::linkOrButton($url, $title . $orderImg, $orderLinkParams); + return self::linkOrButton('db_structure.php', $urlParams, $title . $orderImg, $orderLinkParams); } /** diff --git a/libraries/common.inc.php b/libraries/common.inc.php index 8ec80a9638..6705fd08be 100644 --- a/libraries/common.inc.php +++ b/libraries/common.inc.php @@ -132,6 +132,8 @@ if (! defined('PMA_NO_SESSION')) { Session::setUp($GLOBALS['PMA_Config'], $GLOBALS['error_handler']); } +Core::populateRequestWithEncryptedQueryParams(); + /** * init some variables LABEL_variables_init */ diff --git a/libraries/config.default.php b/libraries/config.default.php index fcd54c9a18..8f4f296b70 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -825,6 +825,20 @@ $cfg['UseDbSearch'] = true; $cfg['IgnoreMultiSubmitErrors'] = false; /** + * Define whether phpMyAdmin will encrypt sensitive data from the URL query string. + * + * @global bool $cfg['URLQueryEncryption'] + */ +$cfg['URLQueryEncryption'] = false; + +/** + * A secret key used to encrypt/decrypt the URL query string. Should be 32 bytes long. + * + * @global string $cfg['URLQueryEncryptionSecretKey'] + */ +$cfg['URLQueryEncryptionSecretKey'] = ''; + +/** * allow login to any user entered server in cookie based authentication * * @global boolean $cfg['AllowArbitraryServer'] |