diff --git a/ChangeLog b/ChangeLog index 4ee0814bfc..e8b9d707a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,7 +24,46 @@ phpMyAdmin - ChangeLog - issue #12218 Allow to specify further parameters for control connection - issue #12162 Show charset for each table on Database structure page -4.6.4 (not yet released) +4.6.5 (not yet released) + +4.6.4 (2016-08-16) +- issue [security] Weaknesses with cookie encryption, see PMASA-2016-29 +- issue [security] Improve session cookie code for openid.php and signon.php example files +- issue [security] Full path disclosure in openid.php and signon.php example files +- issue [security] Multiple XSS vulnerabilities, see PMASA-2016-30 +- issue [security] Multiple XSS vulnerabilities, see PMASA-2016-31 +- issue [security] Unsafe generation of BlowfishSecret (when not supplied by the user) +- issue [security] Referrer leak when phpinfo is enabled +- issue [security] PHP code injection, see PMASA-2016-32 +- issue [security] Full path disclosure, see PMASA-2016-33 +- issue [security] SQL injection attack, see PMASA-2016-34 +- issue [security] Local file exposure through LOAD DATA LOCAL INFILE, see PMASA-2016-35 +- issue [security] Local file exposure through symlinks with UploadDir, see PMASA-2016-36 +- issue [security] Path traversal with SaveDir and UploadDir, see PMASA-2016-37 +- issue [security] Multiple XSS vulnerabilities, see PMASA-2016-38 +- issue [security] SQL injection vulnerability as control user, see PMASA-2016-39 +- issue [security] SQL injection vulnerability, see PMASA-2016-40 +- issue [security] Denial-of-service attack through transformation feature, see PMASA-2016-41 +- issue [security] SQL injection vulnerability as control user, see PMASA-2016-42 +- issue [security] Verify data before unserializing, see PMASA-2016-43 +- issue [security] Use HTTPS for wiki links +- issue Remove Swekey support +- issue [security] SSRF in setup script, see PMASA-2016-44 +- issue [security] Denial-of-service attack with $cfg['AllowArbitraryServer'] = true and persistent connections, see PMASA-2016-45 +- issue [security] Improve SSL certificate handling +- issue [security] Fix full path disclosure in debugging code +- issue [security] Possible circumvention of IP-based allow/deny rules with IPv6 and proxy server, see PMASA-2016-47 +- issue [security] Detect if user is logged in, see PMASA-2016-48 +- issue [security] Bypass URL redirection protection, see PMASA-2016-49 +- issue [security] Referrer leak, see PMASA-2016-50 +- issue [security] Reflected File Download, see PMASA-2016-51 +- issue [security] ArbitraryServerRegexp bypass, see PMASA-2016-52 +- issue [security] Denial-of-service attack by entering long password, see PMASA-2016-53 +- issue [security] Remote code execution vulnerability when running as CGI, see PMASA-2016-054 +- issue [security] Administrators could trigger SQL injection attack against users +- issue [security] Denial-of-service attack when PHP uses dbase extension, see PMASA-2016-55 +- issue [security] Remove tode execution vulnerability when PHP uses dbase extension, see PMASA-2016-56 +- issue [security] Denial-of-service attack by using for loops, see PMASA-2016-46 - issue Include X-Robots-Tag header in responses - issue Enforce numeric field length when creating table - issue Fixed invalid Content-Length in some HTTP responses diff --git a/changelog.php b/changelog.php index b4a612487b..db654c08e7 100644 --- a/changelog.php +++ b/changelog.php @@ -13,6 +13,7 @@ require 'libraries/common.inc.php'; $response = PMA\libraries\Response::getInstance(); $response->disable(); +$response->getHeader()->sendHttpHeaders(); $filename = CHANGELOG_FILE; @@ -155,6 +156,7 @@ echo ''; var links = document.getElementsByTagName("a"); for(var i = 0; i < links.length; i++) { links[i].target = "_blank"; + links[i].rel = "noopener noreferrer"; } diff --git a/config.sample.inc.php b/config.sample.inc.php index 6c904b49d4..578ae841ea 100644 --- a/config.sample.inc.php +++ b/config.sample.inc.php @@ -12,7 +12,7 @@ /** * This is needed for cookie based authentication to encrypt password in - * cookie + * cookie. Needs to be 32 chars long. */ $cfg['blowfish_secret'] = ''; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */ diff --git a/doc/config.rst b/doc/config.rst index 217be5ced7..91d867c1e1 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1631,7 +1631,10 @@ Cookie authentication options The "cookie" auth\_type uses AES algorithm to encrypt the password. If you are using the "cookie" auth\_type, enter here a random passphrase of your choice. It will be used internally by the AES algorithm: you won’t be - prompted for this passphrase. There is no maximum length for this secret. + prompted for this passphrase. + + The secret should be 32 characters long. Using shorter will lead to weaker security + of encrypted cookies, using longer will cause no harm. .. note:: @@ -1991,11 +1994,6 @@ Main panel You can additionally hide more information by using :config:option:`$cfg['Servers'][$i]['verbose']`. -.. config:option:: $cfg['ShowPhpInfo'] - - :type: boolean - :default: false - .. config:option:: $cfg['ShowChgPassword'] :type: boolean @@ -2006,18 +2004,11 @@ Main panel :type: boolean :default: true - Defines whether to display the :guilabel:`PHP information` and + Defines whether to display the :guilabel:`Change password` links and form for creating database or not at the starting main (right) frame. This setting does not check MySQL commands entered directly. - Please note that to block the usage of ``phpinfo()`` in scripts, you have to - put this in your :file:`php.ini`: - - .. code-block:: ini - - disable_functions = phpinfo() - Also note that enabling the :guilabel:`Change password` link has no effect with config authentication mode: because of the hard coded password value in the configuration file, end users can't be allowed to change their @@ -3087,7 +3078,6 @@ Developer Enable to let server present itself as demo server. This is used for `phpMyAdmin demo server `_. - .. _config-examples: Examples diff --git a/doc/setup.rst b/doc/setup.rst index 3a8fae374a..e1d5cc3e77 100644 --- a/doc/setup.rst +++ b/doc/setup.rst @@ -288,7 +288,8 @@ simple configuration may look like this: 'Signon test'); $id = session_id(); /* Close that session */ - session_write_close(); + @session_write_close(); /* Redirect to phpMyAdmin (should use absolute URL here!) */ header('Location: ../index.php'); } else { diff --git a/import.php b/import.php index 39db9643a6..83bfa76fe0 100644 --- a/import.php +++ b/import.php @@ -11,6 +11,11 @@ use PMA\libraries\File; use PMA\libraries\URL; use PMA\libraries\Bookmark; +/* Enable LOAD DATA LOCAL INFILE for LDI plugin */ +if (isset($_POST['format']) && $_POST['format'] == 'ldi') { + define('PMA_ENABLE_LDI', 1); +} + /** * Get the variables sent or posted to this script and a core script */ @@ -445,6 +450,15 @@ if (! empty($local_import_file) && ! empty($cfg['UploadDir'])) { $import_file = PMA\libraries\Util::userDir($cfg['UploadDir']) . $local_import_file; + /* + * Do not allow symlinks to avoid security issues + * (user can create symlink to file he can not access, + * but phpMyAdmin can). + */ + if (@is_link($import_file)) { + $import_file = 'none'; + } + } elseif (empty($import_file) || ! is_uploaded_file($import_file)) { $import_file = 'none'; } diff --git a/index.php b/index.php index b767b0887f..91196c12f5 100644 --- a/index.php +++ b/index.php @@ -321,7 +321,7 @@ if ($server > 0 && $GLOBALS['cfg']['ShowServerInfo']) { . ' '; } -if ($GLOBALS['cfg']['ShowServerInfo'] || $GLOBALS['cfg']['ShowPhpInfo']) { +if ($GLOBALS['cfg']['ShowServerInfo']) { echo '
'; echo '

' , __('Web server') , '

'; echo ''; echo '
'; } @@ -523,15 +514,23 @@ if ($GLOBALS['cfg']['LoginCookieStore'] != 0 /** * Check if user does not have defined blowfish secret and it is being used. */ -if (! empty($_SESSION['encryption_key']) - && empty($GLOBALS['cfg']['blowfish_secret']) -) { - trigger_error( - __( - 'The configuration file now needs a secret passphrase (blowfish_secret).' - ), - E_USER_WARNING - ); +if (! empty($_SESSION['encryption_key'])) { + if (empty($GLOBALS['cfg']['blowfish_secret'])) { + trigger_error( + __( + 'The configuration file now needs a secret passphrase (blowfish_secret).' + ), + E_USER_WARNING + ); + } + if (strlen($GLOBALS['cfg']['blowfish_secret']) < 32) { + trigger_error( + __( + 'The secret passphrase in configuration (blowfish_secret) is too short.' + ), + E_USER_WARNING + ); + } } /** diff --git a/js/functions.js b/js/functions.js index 725e74be93..df8589b562 100644 --- a/js/functions.js +++ b/js/functions.js @@ -4117,7 +4117,16 @@ AJAX.registerOnload('functions.js', function () { * Load version information asynchronously. */ if ($('li.jsversioncheck').length > 0) { - $.getJSON('version_check.php', {'server' : PMA_commonParams.get('server')}, PMA_current_version); + $.ajax({ + dataType: "json", + url: 'version_check.php', + method: "POST", + data: { + "server": PMA_commonParams.get('server'), + "token": PMA_commonParams.get('token'), + }, + success: PMA_current_version + }); } if ($('#is_git_revision').length > 0) { diff --git a/js/tbl_relation.js b/js/tbl_relation.js index 130fb457c5..e6498ee5d2 100644 --- a/js/tbl_relation.js +++ b/js/tbl_relation.js @@ -23,7 +23,7 @@ function setDropdownValues($dropdown, values, selectedValue) { // add an empty string to the beginning for empty selection values.unshift(''); $.each(values, function () { - optionsAsString += ""; + optionsAsString += ""; }); $dropdown.append($(optionsAsString)); } diff --git a/libraries/DbSearch.php b/libraries/DbSearch.php index 2bb24704df..446d86d914 100644 --- a/libraries/DbSearch.php +++ b/libraries/DbSearch.php @@ -356,10 +356,10 @@ class DbSearch $html_output .= '' . __('Delete') . ''; } else { diff --git a/libraries/Error.php b/libraries/Error.php index 5caec03852..6548877c60 100644 --- a/libraries/Error.php +++ b/libraries/Error.php @@ -117,12 +117,17 @@ class Error extends Message { $result = array(); - $members = array('file', 'line', 'function', 'class', 'type'); + $members = array('line', 'function', 'class', 'type'); foreach ($backtrace as $idx => $step) { /* Create new backtrace entry */ $result[$idx] = array(); + /* Make path relative */ + if (isset($step['file'])) { + $result[$idx]['file'] = Error::relPath($step['file']); + } + /* Store members we want */ foreach ($members as $name) { if (isset($step[$name])) { diff --git a/libraries/File.php b/libraries/File.php index 90e5672800..f20e93fa15 100644 --- a/libraries/File.php +++ b/libraries/File.php @@ -420,6 +420,11 @@ class File $this->setName( Util::userDir($GLOBALS['cfg']['UploadDir']) . PMA_securePath($name) ); + if (@is_link($this->getName())) { + $this->_error_message = __('File is a symbolic link'); + $this->setName(null); + return false; + } if (! $this->isReadable()) { $this->_error_message = Message::error(__('File could not be read!')); $this->setName(null); diff --git a/libraries/Logging.php b/libraries/Logging.php index b12c376b41..d8d018028e 100644 --- a/libraries/Logging.php +++ b/libraries/Logging.php @@ -35,7 +35,7 @@ class Logging @syslog( LOG_WARNING, 'user denied: ' . $user . ' (' . $status . ') from ' . - $_SERVER['REMOTE_ADDR'] + PMA_getIp() ); } } diff --git a/libraries/RecentFavoriteTable.php b/libraries/RecentFavoriteTable.php index 4cbf4b3fba..b65d4da1bc 100644 --- a/libraries/RecentFavoriteTable.php +++ b/libraries/RecentFavoriteTable.php @@ -98,7 +98,7 @@ class RecentFavoriteTable // Read from phpMyAdmin database, if recent tables is not in session $sql_query = " SELECT `tables` FROM " . $this->_getPmaTable() . - " WHERE `username` = '" . $GLOBALS['cfg']['Server']['user'] . "'"; + " WHERE `username` = '" . Util::sqlAddSlashes($GLOBALS['cfg']['Server']['user']) . "'"; $return = array(); $result = PMA_queryAsControlUser($sql_query, false); @@ -121,7 +121,7 @@ class RecentFavoriteTable $username = $GLOBALS['cfg']['Server']['user']; $sql_query = " REPLACE INTO " . $this->_getPmaTable() . " (`username`, `tables`)" . - " VALUES ('" . $username . "', '" + " VALUES ('" . Util::sqlAddSlashes($username) . "', '" . Util::sqlAddSlashes( json_encode($this->_tables) ) . "')"; diff --git a/libraries/Response.php b/libraries/Response.php index bdeb57995c..825142daf8 100644 --- a/libraries/Response.php +++ b/libraries/Response.php @@ -60,6 +60,13 @@ class Response * @var bool */ private $_isAjax; + /** + * Whether response object is disabled + * + * @access private + * @var bool + */ + private $_isDisabled; /** * Whether we are servicing an ajax request for a page * that was fired using the generic page handler in JS. @@ -102,6 +109,7 @@ class Response $this->_isSuccess = true; $this->_isAjax = false; $this->_isAjaxPage = false; + $this->_isDisabled = false; if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) { $this->_isAjax = true; } @@ -174,6 +182,7 @@ class Response { $this->_header->disable(); $this->_footer->disable(); + $this->_isDisabled = true; } /** @@ -277,6 +286,12 @@ class Response */ private function _ajaxResponse() { + /* Avoid wrapping in case we're disabled */ + if ($this->_isDisabled) { + echo $this->_getDisplay(); + return; + } + if (! isset($this->_JSON['message'])) { $this->_JSON['message'] = $this->_getDisplay(); } else if ($this->_JSON['message'] instanceof Message) { diff --git a/libraries/Sanitize.php b/libraries/Sanitize.php index 568e84560a..ce65f457f0 100644 --- a/libraries/Sanitize.php +++ b/libraries/Sanitize.php @@ -64,6 +64,9 @@ class Sanitize $target = ''; if (! empty($found[3])) { $target = ' target="' . $found[3] . '"'; + if ($found[3] == '_blank') { + $target .= ' rel="noopener noreferrer"'; + } } /* Construct url */ diff --git a/libraries/Table.php b/libraries/Table.php index 5bc6ea187d..c2f5ea3d24 100644 --- a/libraries/Table.php +++ b/libraries/Table.php @@ -1460,7 +1460,7 @@ class Table // Read from phpMyAdmin database $sql_query = " SELECT `prefs` FROM " . $pma_table - . " WHERE `username` = '" . $GLOBALS['cfg']['Server']['user'] . "'" + . " WHERE `username` = '" . Util::sqlAddSlashes($GLOBALS['cfg']['Server']['user']) . "'" . " AND `db_name` = '" . Util::sqlAddSlashes($this->_db_name) . "'" . " AND `table_name` = '" . Util::sqlAddSlashes($this->_name) . "'"; @@ -1488,7 +1488,7 @@ class Table $username = $GLOBALS['cfg']['Server']['user']; $sql_query = " REPLACE INTO " . $pma_table . " (username, db_name, table_name, prefs) VALUES ('" - . $username . "', '" . $secureDbName + . Util::sqlAddSlashes($username) . "', '" . $secureDbName . "', '" . Util::sqlAddSlashes($this->_name) . "', '" . Util::sqlAddSlashes(json_encode($this->uiprefs)) . "')"; diff --git a/libraries/Util.php b/libraries/Util.php index a2c82f57f8..aa8cc94a87 100644 --- a/libraries/Util.php +++ b/libraries/Util.php @@ -2687,7 +2687,7 @@ class Util $dir .= '/'; } - return str_replace('%u', $GLOBALS['cfg']['Server']['user'], $dir); + return str_replace('%u', PMA_securePath($GLOBALS['cfg']['Server']['user']), $dir); } /** diff --git a/libraries/VersionInformation.php b/libraries/VersionInformation.php index ce81bdc644..dcfa362991 100644 --- a/libraries/VersionInformation.php +++ b/libraries/VersionInformation.php @@ -51,20 +51,7 @@ class VersionInformation session_write_close(); } $file = 'https://www.phpmyadmin.net/home_page/version.json'; - if (ini_get('allow_url_fopen')) { - $context = array( - 'http' => array( - 'request_fulluri' => true, - 'timeout' => $connection_timeout, - ) - ); - $context = Util::handleContext($context); - $response = file_get_contents( - $file, - false, - stream_context_create($context) - ); - } else if (function_exists('curl_init')) { + if (function_exists('curl_init')) { $curl_handle = curl_init($file); if ($curl_handle === false) { return null; @@ -86,6 +73,19 @@ class VersionInformation $connection_timeout ); $response = curl_exec($curl_handle); + } else if (ini_get('allow_url_fopen')) { + $context = array( + 'http' => array( + 'request_fulluri' => true, + 'timeout' => $connection_timeout, + ) + ); + $context = Util::handleContext($context); + $response = file_get_contents( + $file, + false, + stream_context_create($context) + ); } } diff --git a/libraries/common.inc.php b/libraries/common.inc.php index 3102e8c827..a9e522f740 100644 --- a/libraries/common.inc.php +++ b/libraries/common.inc.php @@ -292,7 +292,6 @@ $goto_whitelist = array( 'index.php', 'pdf_pages.php', 'pdf_schema.php', - //'phpinfo.php', 'server_binlog.php', 'server_collations.php', 'server_databases.php', diff --git a/libraries/config.default.php b/libraries/config.default.php index 508c265f49..1d3c40bb1e 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -1038,13 +1038,6 @@ $cfg['NavigationTreeShowEvents'] = true; */ $cfg['ShowStats'] = true; -/** - * show PHP info link - * - * @global boolean $cfg['ShowPhpInfo'] - */ -$cfg['ShowPhpInfo'] = false; - /** * show MySQL server and web server information * diff --git a/libraries/config/ServerConfigChecks.php b/libraries/config/ServerConfigChecks.php index d0830b79ee..d97d702a18 100644 --- a/libraries/config/ServerConfigChecks.php +++ b/libraries/config/ServerConfigChecks.php @@ -216,10 +216,22 @@ class ServerConfigChecks $blowfishSecret, $cookieAuthServer, $blowfishSecretSet ) { if ($cookieAuthServer && $blowfishSecret === null) { - $blowfishSecret = uniqid('', true); + $blowfishSecret = ''; + if (! function_exists('openssl_random_pseudo_bytes')) { + $random_func = 'phpseclib\\Crypt\\Random::string'; + } else { + $random_func = 'openssl_random_pseudo_bytes'; + } + while (strlen($blowfishSecret) < 32) { + $byte = $random_func(1); + // We want only ASCII chars + if (ord($byte) > 32 && ord($byte) < 127) { + $blowfishSecret .= $byte; + } + } + $blowfishSecretSet = true; $this->cfg->set('blowfish_secret', $blowfishSecret); - return array($blowfishSecret, $blowfishSecretSet); } return array($blowfishSecret, $blowfishSecretSet); } @@ -331,10 +343,10 @@ class ServerConfigChecks } else { $blowfishWarnings = array(); // check length - if (mb_strlen($blowfishSecret) < 8) { + if (strlen($blowfishSecret) < 32) { // too short key $blowfishWarnings[] = __( - 'Key is too short, it should have at least 8 characters.' + 'Key is too short, it should have at least 32 characters.' ); } // check used characters diff --git a/libraries/config/Validator.php b/libraries/config/Validator.php index eb8e6aa2b5..0825e24b2b 100644 --- a/libraries/config/Validator.php +++ b/libraries/config/Validator.php @@ -183,6 +183,7 @@ class Validator ) { // static::testPHPErrorMsg(); $error = null; + $host = PMA_sanitizeMySQLHost($host); if (DatabaseInterface::checkDbExtension('mysqli')) { $socket = empty($socket) || $connect_type == 'tcp' ? null : $socket; diff --git a/libraries/config/messages.inc.php b/libraries/config/messages.inc.php index 5fa0d9970f..3cb93604a0 100644 --- a/libraries/config/messages.inc.php +++ b/libraries/config/messages.inc.php @@ -871,11 +871,6 @@ $strConfigShowFunctionFields_desc = __( $strConfigShowFunctionFields_name = __('Show function fields'); $strConfigShowHint_desc = __('Whether to show hint or not.'); $strConfigShowHint_name = __('Show hint'); -$strConfigShowPhpInfo_desc = __( - 'Shows link to [a@https://php.net/manual/function.phpinfo.php]phpinfo()[/a] ' . - 'output.' -); -$strConfigShowPhpInfo_name = __('Show phpinfo() link'); $strConfigShowServerInfo_name = __('Show detailed MySQL server information'); $strConfigShowSQL_desc = __( 'Defines whether SQL queries generated by phpMyAdmin should be displayed.' diff --git a/libraries/config/setup.forms.php b/libraries/config/setup.forms.php index 1ae2fb406a..a509bf94dd 100644 --- a/libraries/config/setup.forms.php +++ b/libraries/config/setup.forms.php @@ -204,7 +204,6 @@ $forms['Main_panel']['Startup'] = array( 'ShowCreateDb', 'ShowStats', 'ShowServerInfo', - 'ShowPhpInfo', 'ShowChgPassword'); $forms['Main_panel']['DbStructure'] = array( 'ShowDbStructureCharset', diff --git a/libraries/core.lib.php b/libraries/core.lib.php index efc7994a14..a6a41d68ac 100644 --- a/libraries/core.lib.php +++ b/libraries/core.lib.php @@ -501,11 +501,6 @@ function PMA_sendHeaderLocation($uri, $use_refresh = false) session_write_close(); if ($response->headersSent()) { - if (function_exists('debug_print_backtrace')) { - echo '
';
-            debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
-            echo '
'; - } trigger_error( 'PMA_sendHeaderLocation called when headers are already sent!', E_USER_ERROR @@ -735,6 +730,10 @@ function PMA_linkURL($url) function PMA_isAllowedDomain($url) { $arr = parse_url($url); + // Avoid URLs without hostname or with credentials + if (empty($arr['host']) || ! empty($arr['user']) || ! empty($arr['pass'])) { + return false; + } $domain = $arr["host"]; $domainWhiteList = array( /* Include current domain */ @@ -953,6 +952,50 @@ function PMA_checkExtensions() } } +/** + * Gets the "true" IP address of the current user + * + * @return string the ip of the user + * + * @access private + */ +function PMA_getIp() +{ + /* Get the address of user */ + if (empty($_SERVER['REMOTE_ADDR'])) { + /* We do not know remote IP */ + return false; + } + + $direct_ip = $_SERVER['REMOTE_ADDR']; + + /* Do we trust this IP as a proxy? If yes we will use it's header. */ + if (!isset($GLOBALS['cfg']['TrustedProxies'][$direct_ip])) { + /* Return true IP */ + return $direct_ip; + } + + /** + * Parse header in form: + * X-Forwarded-For: client, proxy1, proxy2 + */ + // Get header content + $value = PMA_getenv($GLOBALS['cfg']['TrustedProxies'][$direct_ip]); + // Grab first element what is client adddress + $value = explode(',', $value)[0]; + // checks that the header contains only one IP address, + $is_ip = filter_var($value, FILTER_VALIDATE_IP); + + if ($is_ip !== false) { + // True IP behind a proxy + return $value; + } + + // We could not parse header + return false; +} // end of the 'PMA_getIp()' function + + /* Compatibility with PHP < 5.6 */ if(! function_exists('hash_equals')) { @@ -970,3 +1013,135 @@ if(! function_exists('hash_equals')) { return ! $ret; } } +/* Compatibility with PHP < 5.1 or PHP without hash extension */ +if (! function_exists('hash_hmac')) { + function hash_hmac($algo, $data, $key, $raw_output = false) + { + $algo = strtolower($algo); + $pack = 'H'.strlen($algo('test')); + $size = 64; + $opad = str_repeat(chr(0x5C), $size); + $ipad = str_repeat(chr(0x36), $size); + + if (strlen($key) > $size) { + $key = str_pad(pack($pack, $algo($key)), $size, chr(0x00)); + } else { + $key = str_pad($key, $size, chr(0x00)); + } + + for ($i = 0; $i < strlen($key) - 1; $i++) { + $opad[$i] = $opad[$i] ^ $key[$i]; + $ipad[$i] = $ipad[$i] ^ $key[$i]; + } + + $output = $algo($opad.pack($pack, $algo($ipad.$data))); + + return ($raw_output) ? pack($pack, $output) : $output; + } +} + +/** + * Sanitizes MySQL hostname + * + * * strips p: prefix + * + * @param string $name User given hostname + * + * @return string + */ +function PMA_sanitizeMySQLHost($name) +{ + if (strtolower(substr($name, 0, 2)) == 'p:') { + return substr($name, 2); + } + + return $name; +} + +/** + * Safe unserializer wrapper + * + * It does not unserialize data containing objects + * + * @param string $data Data to unserialize + * + * @return mixed + */ +function PMA_safeUnserialize($data) +{ + if (! is_string($data)) { + return null; + } + + /* validate serialized data */ + $length = strlen($data); + $depth = 0; + for ($i = 0; $i < $length; $i++) { + $value = $data[$i]; + + switch ($value) + { + case '}': + /* end of array */ + if ($depth <= 0) { + return null; + } + $depth--; + break; + case 's': + /* string */ + // parse sting length + $strlen = intval($data[$i + 2]); + // string start + $i = strpos($data, ':', $i + 2); + if ($i === false) { + return null; + } + // skip string, quotes and ; + $i += 2 + $strlen + 1; + if ($data[$i] != ';') { + return null; + } + break; + + case 'b': + case 'i': + case 'd': + /* bool, integer or double */ + // skip value to sepearator + $i = strpos($data, ';', $i); + if ($i === false) { + return null; + } + break; + case 'a': + /* array */ + // find array start + $i = strpos($data, '{', $i); + if ($i === false) { + return null; + } + // remember nesting + $depth++; + break; + case 'N': + /* null */ + // skip to end + $i = strpos($data, ';', $i); + if ($i === false) { + return null; + } + break; + default: + /* any other elements are not wanted */ + return null; + } + } + + // check unterminated arrays + if ($depth > 0) { + return null; + } + + return unserialize($data); +} diff --git a/libraries/create_addfield.lib.php b/libraries/create_addfield.lib.php index 7c6726d892..f3960017a6 100644 --- a/libraries/create_addfield.lib.php +++ b/libraries/create_addfield.lib.php @@ -446,18 +446,19 @@ function PMA_getTableCreationQuery($db, $table) function PMA_getNumberOfFieldsFromRequest() { if (isset($_REQUEST['submit_num_fields'])) { // adding new fields - $num_fields = $_REQUEST['orig_num_fields'] + $_REQUEST['added_fields']; + $num_fields = intval($_REQUEST['orig_num_fields']) + intval($_REQUEST['added_fields']); } elseif (isset($_REQUEST['orig_num_fields'])) { // retaining existing fields - $num_fields = $_REQUEST['orig_num_fields']; + $num_fields = intval($_REQUEST['orig_num_fields']); } elseif (isset($_REQUEST['num_fields']) && intval($_REQUEST['num_fields']) > 0 ) { // new table with specified number of fields - $num_fields = (int) $_REQUEST['num_fields']; + $num_fields = intval($_REQUEST['num_fields']); } else { // new table with unspecified number of fields $num_fields = 4; } - return $num_fields; + // Limit to 4096 fields (MySQL maximal value) + return min($num_fields, 4096); } /** diff --git a/libraries/db_designer.lib.php b/libraries/db_designer.lib.php index 34e2028d13..bf52111d59 100644 --- a/libraries/db_designer.lib.php +++ b/libraries/db_designer.lib.php @@ -90,7 +90,7 @@ function PMA_getPageIdsAndNames($db) $result = array(); while ($curr_page = $GLOBALS['dbi']->fetchAssoc($page_rs)) { - $result[$curr_page['page_nr']] = $curr_page['page_descr']; + $result[intval($curr_page['page_nr'])] = $curr_page['page_descr']; } return $result; } diff --git a/libraries/dbi/DBIMysql.php b/libraries/dbi/DBIMysql.php index 0546ab9d49..ebeb9c5e2b 100644 --- a/libraries/dbi/DBIMysql.php +++ b/libraries/dbi/DBIMysql.php @@ -115,10 +115,12 @@ class DBIMysql implements DBIExtension $client_flags = 0; - // always use CLIENT_LOCAL_FILES as defined in mysql_com.h - // for the case where the client library was not compiled - // with --enable-local-infile - $client_flags |= 128; + if (defined('PMA_ENABLE_LDI')) { + // use CLIENT_LOCAL_FILES as defined in mysql_com.h + // for the case where the client library was not compiled + // with --enable-local-infile + $client_flags |= 128; + } /* Optionally compress connection */ if (defined('MYSQL_CLIENT_COMPRESS') && $server['compress']) { diff --git a/libraries/dbi/DBIMysqli.php b/libraries/dbi/DBIMysqli.php index 485b649a76..8ff31ba228 100644 --- a/libraries/dbi/DBIMysqli.php +++ b/libraries/dbi/DBIMysqli.php @@ -129,7 +129,11 @@ class DBIMysqli implements DBIExtension $link = mysqli_init(); - mysqli_options($link, MYSQLI_OPT_LOCAL_INFILE, true); + if (defined('PMA_ENABLE_LDI')) { + mysqli_options($link, MYSQLI_OPT_LOCAL_INFILE, true); + } else { + mysqli_options($link, MYSQLI_OPT_LOCAL_INFILE, false); + } $client_flags = 0; diff --git a/libraries/display_export.lib.php b/libraries/display_export.lib.php index 44f16159cf..7c282c4689 100644 --- a/libraries/display_export.lib.php +++ b/libraries/display_export.lib.php @@ -252,7 +252,7 @@ function PMA_getOptionsForExportTemplates($export_type) . PMA\libraries\Util::backquote($cfgRelation['export_templates']) . " WHERE `username` = " . "'" . PMA\libraries\Util::sqlAddSlashes($GLOBALS['cfg']['Server']['user']) - . "' AND `export_type` = '" . $export_type . "'" + . "' AND `export_type` = '" . PMA\libraries\Util::sqlAddSlashes($export_type) . "'" . " ORDER BY `template_name`;"; $result = PMA_queryAsControlUser($query); diff --git a/libraries/display_git_revision.lib.php b/libraries/display_git_revision.lib.php index bd179973dc..38506c8889 100644 --- a/libraries/display_git_revision.lib.php +++ b/libraries/display_git_revision.lib.php @@ -37,7 +37,7 @@ function PMA_printGitRevision() 'https://github.com/phpmyadmin/phpmyadmin/commit/' . $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_COMMITHASH') ) - . '" target="_blank">' . $commit_hash . ''; + . '" rel="noopener noreferrer" target="_blank">' . $commit_hash . ''; } $branch = $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_BRANCH'); @@ -47,7 +47,7 @@ function PMA_printGitRevision() 'https://github.com/phpmyadmin/phpmyadmin/tree/' . $GLOBALS['PMA_Config']->get('PMA_VERSION_GIT_BRANCH') ) - . '" target="_blank">' . $branch . ''; + . '" rel="noopener noreferrer" target="_blank">' . $branch . ''; } if ($branch !== false) { $branch = sprintf(__('%1$s from %2$s branch'), $commit_hash, $branch); diff --git a/libraries/engines/Pbxt.php b/libraries/engines/Pbxt.php index 1922ea42a7..68af617199 100644 --- a/libraries/engines/Pbxt.php +++ b/libraries/engines/Pbxt.php @@ -182,7 +182,7 @@ class Pbxt extends StorageEngine . ' can be found on the %sPrimeBase XT Home Page%s.' ), '', + . '" rel="noopener noreferrer" target="_blank">', '' ) . '

' . "\n"; diff --git a/libraries/error_report.lib.php b/libraries/error_report.lib.php index 937e71547f..67a4c9aa63 100644 --- a/libraries/error_report.lib.php +++ b/libraries/error_report.lib.php @@ -179,7 +179,24 @@ function PMA_sanitizeUrl($url) function PMA_sendErrorReport($report) { $data_string = json_encode($report); - if (ini_get('allow_url_fopen')) { + if (function_exists('curl_init')) { + $curl_handle = curl_init(SUBMISSION_URL); + if ($curl_handle === false) { + return null; + } + $curl_handle = PMA\libraries\Util::configureCurl($curl_handle); + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, "POST"); + curl_setopt( + $curl_handle, CURLOPT_HTTPHEADER, + array('Expect:', 'Content-Type: application/json') + ); + curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $data_string); + curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($curl_handle); + curl_close($curl_handle); + + return $response; + } else if (ini_get('allow_url_fopen')) { $context = array("http" => array( 'method' => 'POST', @@ -196,26 +213,7 @@ function PMA_sendErrorReport($report) return $response; } - if (!function_exists('curl_init')) { - return null; - } - - $curl_handle = curl_init(SUBMISSION_URL); - if ($curl_handle === false) { - return null; - } - $curl_handle = PMA\libraries\Util::configureCurl($curl_handle); - curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, "POST"); - curl_setopt( - $curl_handle, CURLOPT_HTTPHEADER, - array('Expect:', 'Content-Type: application/json') - ); - curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $data_string); - curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1); - $response = curl_exec($curl_handle); - curl_close($curl_handle); - - return $response; + return null; } /** diff --git a/libraries/file_listing.lib.php b/libraries/file_listing.lib.php index 64f7230634..64b469b014 100644 --- a/libraries/file_listing.lib.php +++ b/libraries/file_listing.lib.php @@ -26,6 +26,7 @@ function PMA_getDirContent($dir, $expression = '') } while ($file = @readdir($handle)) { if (@is_file($dir . $file) + && ! @is_link($dir . $file) && ($expression == '' || preg_match($expression, $file)) ) { $result[] = $file; diff --git a/libraries/gis/GISGeometry.php b/libraries/gis/GISGeometry.php index 826cd92d24..e84a405dae 100644 --- a/libraries/gis/GISGeometry.php +++ b/libraries/gis/GISGeometry.php @@ -129,11 +129,11 @@ abstract class GISGeometry . 'bound.extend(new OpenLayers.LonLat(' . $scale_data['minX'] . ', ' . $scale_data['minY'] . ').transform(new OpenLayers.Projection("EPSG:' - . $srid . '"), map.getProjectionObject())); ' + . intval($srid) . '"), map.getProjectionObject())); ' . 'bound.extend(new OpenLayers.LonLat(' . $scale_data['maxX'] . ', ' . $scale_data['maxY'] . ').transform(new OpenLayers.Projection("EPSG:' - . $srid . '"), map.getProjectionObject()));'; + . intval($srid) . '"), map.getProjectionObject()));'; } /** @@ -222,7 +222,7 @@ abstract class GISGeometry // Extract coordinates of the point $cordinates = explode(" ", $point); - if (isset($cordinates[0]) && trim($cordinates[0]) != '' + if (!empty($cordinates[0]) && trim($cordinates[0]) != '' && isset($cordinates[1]) && trim($cordinates[1]) != '' ) { @@ -231,8 +231,8 @@ abstract class GISGeometry $y = $scale_data['height'] - ($cordinates[1] - $scale_data['y']) * $scale_data['scale']; } else { - $x = trim($cordinates[0]); - $y = trim($cordinates[1]); + $x = floatval(trim($cordinates[0])); + $y = floatval(trim($cordinates[1])); } } else { $x = ''; @@ -394,6 +394,6 @@ abstract class GISGeometry { return '(new OpenLayers.Geometry.Point(' . $point[0] . ',' . $point[1] . '))' . '.transform(new OpenLayers.Projection("EPSG:' - . $srid . '"), map.getProjectionObject())'; + . intval($srid) . '"), map.getProjectionObject())'; } } diff --git a/libraries/ip_allow_deny.lib.php b/libraries/ip_allow_deny.lib.php index 120463307a..6d4b9937bb 100644 --- a/libraries/ip_allow_deny.lib.php +++ b/libraries/ip_allow_deny.lib.php @@ -7,45 +7,6 @@ * @package PhpMyAdmin */ -/** - * Gets the "true" IP address of the current user - * - * @return string the ip of the user - * - * @access private - */ -function PMA_getIp() -{ - /* Get the address of user */ - if (empty($_SERVER['REMOTE_ADDR'])) { - /* We do not know remote IP */ - return false; - } - - $direct_ip = $_SERVER['REMOTE_ADDR']; - - /* Do we trust this IP as a proxy? If yes we will use it's header. */ - if (!isset($GLOBALS['cfg']['TrustedProxies'][$direct_ip])) { - /* Return true IP */ - return $direct_ip; - } - - $trusted_header_value - = PMA_getenv($GLOBALS['cfg']['TrustedProxies'][$direct_ip]); - $matches = array(); - // checks that the header contains only one IP address, - $is_ip = filter_var($trusted_header_value, FILTER_VALIDATE_IP); - - if ($is_ip !== false) { - // True IP behind a proxy - return $trusted_header_value; - } - - /* Return true IP */ - return $direct_ip; -} // end of the 'PMA_getIp()' function - - /** * Matches for IPv4 or IPv6 addresses * diff --git a/libraries/navigation/NavigationTree.php b/libraries/navigation/NavigationTree.php index 72c58bb45c..dff3af35df 100644 --- a/libraries/navigation/NavigationTree.php +++ b/libraries/navigation/NavigationTree.php @@ -175,7 +175,7 @@ class NavigationTree $query = "SELECT (COUNT(DB_first_level) DIV %d) * %d "; $query .= "from ( "; $query .= " SELECT distinct SUBSTRING_INDEX(SCHEMA_NAME, "; - $query .= " '{$GLOBALS['cfg']['NavigationTreeDbSeparator']}', 1) "; + $query .= " '" . Util::sqlAddSlashes($GLOBALS['cfg']['NavigationTreeDbSeparator']) . "', 1) "; $query .= " DB_first_level "; $query .= " FROM INFORMATION_SCHEMA.SCHEMATA "; $query .= " WHERE `SCHEMA_NAME` < '%s' "; diff --git a/libraries/navigation/nodes/Node.php b/libraries/navigation/nodes/Node.php index 0acc23540e..0e186c6498 100644 --- a/libraries/navigation/nodes/Node.php +++ b/libraries/navigation/nodes/Node.php @@ -434,7 +434,7 @@ class Node $query .= "SELECT DB_first_level "; $query .= "FROM ( "; $query .= "SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, "; - $query .= "'$dbSeparator', 1) "; + $query .= "'" . Util::sqlAddSlashes($dbSeparator) . "', 1) "; $query .= "DB_first_level "; $query .= "FROM INFORMATION_SCHEMA.SCHEMATA "; $query .= $this->_getWhereClause('SCHEMA_NAME', $searchClause); @@ -444,9 +444,9 @@ class Node $query .= ") t2 "; $query .= $this->_getWhereClause('SCHEMA_NAME', $searchClause); $query .= "AND 1 = LOCATE(CONCAT(DB_first_level, "; - $query .= "'$dbSeparator'), "; + $query .= "'" . Util::sqlAddSlashes($dbSeparator) . "'), "; $query .= "CONCAT(SCHEMA_NAME, "; - $query .= "'$dbSeparator')) "; + $query .= "'" . Util::sqlAddSlashes($dbSeparator) . "')) "; $query .= "ORDER BY SCHEMA_NAME ASC"; $retval = $GLOBALS['dbi']->fetchResult($query); diff --git a/libraries/navigation/nodes/NodeDatabase.php b/libraries/navigation/nodes/NodeDatabase.php index 37f1c07fe5..18b48a53f3 100644 --- a/libraries/navigation/nodes/NodeDatabase.php +++ b/libraries/navigation/nodes/NodeDatabase.php @@ -691,7 +691,7 @@ class NodeDatabase extends Node . '' . Util::getImage( 'show.png', diff --git a/libraries/plugin_interface.lib.php b/libraries/plugin_interface.lib.php index 1d165a1bf3..b9fd1b6512 100644 --- a/libraries/plugin_interface.lib.php +++ b/libraries/plugin_interface.lib.php @@ -37,7 +37,10 @@ function PMA_getPlugin( if (is_file($plugins_dir . $file)) { //include_once $plugins_dir . $file; $fqnClass = 'PMA\\' . str_replace('/', '\\', $plugins_dir) . $class_name; - return new $fqnClass; + // check if class exists, could be caused by skip_import + if (class_exists($fqnClass)) { + return new $fqnClass; + } } return null; diff --git a/libraries/plugins/auth/AuthenticationCookie.php b/libraries/plugins/auth/AuthenticationCookie.php index cdab1b296b..b560e6fc8f 100644 --- a/libraries/plugins/auth/AuthenticationCookie.php +++ b/libraries/plugins/auth/AuthenticationCookie.php @@ -286,7 +286,7 @@ class AuthenticationCookie extends AuthenticationPlugin // verify captcha status. $resp = $reCaptcha->verify( $_POST["g-recaptcha-response"], - $_SERVER["REMOTE_ADDR"] + PMA_getIp() ); // Check if the captcha entered is valid, if not stop the login. @@ -324,7 +324,7 @@ class AuthenticationCookie extends AuthenticationPlugin return false; } } - $GLOBALS['pma_auth_server'] = $_REQUEST['pma_servername']; + $GLOBALS['pma_auth_server'] = PMA_sanitizeMySQLHost($_REQUEST['pma_servername']); } PMA_secureSession(); return true; @@ -333,18 +333,8 @@ class AuthenticationCookie extends AuthenticationPlugin // At the end, try to set the $GLOBALS['PHP_AUTH_USER'] // and $GLOBALS['PHP_AUTH_PW'] variables from cookies - // servername - if ($GLOBALS['cfg']['AllowArbitraryServer'] - && ! empty($_COOKIE['pmaServer-' . $GLOBALS['server']]) - ) { - $GLOBALS['pma_auth_server'] - = $_COOKIE['pmaServer-' . $GLOBALS['server']]; - } - // check cookies - if (empty($_COOKIE['pmaUser-' . $GLOBALS['server']]) - || empty($_COOKIE['pma_iv-' . $GLOBALS['server']]) - ) { + if (empty($_COOKIE['pmaUser-' . $GLOBALS['server']])) { return false; } @@ -381,15 +371,26 @@ class AuthenticationCookie extends AuthenticationPlugin } // check password cookie - if (empty($_COOKIE['pmaPass-' . $GLOBALS['server']])) { + if (empty($_COOKIE['pmaAuth-' . $GLOBALS['server']])) { return false; } - $GLOBALS['PHP_AUTH_PW'] = $this->cookieDecrypt( - $_COOKIE['pmaPass-' . $GLOBALS['server']], - $this->_getSessionEncryptionSecret() + $auth_data = json_decode( + $this->cookieDecrypt( + $_COOKIE['pmaAuth-' . $GLOBALS['server']], + $this->_getSessionEncryptionSecret() + ), + true ); + if (! is_array($auth_data) || ! isset($auth_data['password'])) { + return false; + } + $GLOBALS['PHP_AUTH_PW'] = $auth_data['password']; + if ($GLOBALS['cfg']['AllowArbitraryServer'] && ! empty($auth_data['server'])) { + $GLOBALS['pma_auth_server'] = $auth_data['server']; + } + $GLOBALS['from_cookie'] = true; return true; @@ -464,8 +465,6 @@ class AuthenticationCookie extends AuthenticationPlugin { global $cfg; - $this->createIV(); - // Name and password cookies need to be refreshed each time // Duration = one month for username $this->storeUsernameCookie($cfg['Server']['user']); @@ -480,21 +479,6 @@ class AuthenticationCookie extends AuthenticationPlugin // Set server cookies if required (once per session) and, in this case, // force reload to ensure the client accepts cookies if (! $GLOBALS['from_cookie']) { - if ($GLOBALS['cfg']['AllowArbitraryServer']) { - if (! empty($GLOBALS['pma_auth_server'])) { - // Duration = one month for servername - $GLOBALS['PMA_Config']->setCookie( - 'pmaServer-' . $GLOBALS['server'], - $cfg['Server']['host'] - ); - } else { - // Delete servername cookie - $GLOBALS['PMA_Config']->removeCookie( - 'pmaServer-' . $GLOBALS['server'] - ); - } - } - // URL where to go: $redirect_url = './index.php'; @@ -564,11 +548,15 @@ class AuthenticationCookie extends AuthenticationPlugin */ public function storePasswordCookie($password) { + $payload = array('password' => $password); + if ($GLOBALS['cfg']['AllowArbitraryServer'] && ! empty($GLOBALS['pma_auth_server'])) { + $payload['server'] = $GLOBALS['pma_auth_server']; + } // Duration = as configured $GLOBALS['PMA_Config']->setCookie( - 'pmaPass-' . $GLOBALS['server'], + 'pmaAuth-' . $GLOBALS['server'], $this->cookieEncrypt( - $password, + json_encode($payload), $this->_getSessionEncryptionSecret() ), null, @@ -592,7 +580,7 @@ class AuthenticationCookie extends AuthenticationPlugin global $conn_error; // Deletes password cookie and displays the login form - $GLOBALS['PMA_Config']->removeCookie('pmaPass-' . $GLOBALS['server']); + $GLOBALS['PMA_Config']->removeCookie('pmaAuth-' . $GLOBALS['server']); $conn_error = $this->getErrorMessage(); @@ -615,8 +603,7 @@ class AuthenticationCookie extends AuthenticationPlugin if (empty($GLOBALS['cfg']['blowfish_secret'])) { return $this->_getSessionEncryptionSecret(); } else { - // apply md5() to work around too long secrets (returns 32 characters) - return md5($GLOBALS['cfg']['blowfish_secret']); + return $GLOBALS['cfg']['blowfish_secret']; } } @@ -629,9 +616,9 @@ class AuthenticationCookie extends AuthenticationPlugin { if (empty($_SESSION['encryption_key'])) { if (self::useOpenSSL()) { - $_SESSION['encryption_key'] = openssl_random_pseudo_bytes(256); + $_SESSION['encryption_key'] = openssl_random_pseudo_bytes(32); } else { - $_SESSION['encryption_key'] = Crypt\Random::string(256); + $_SESSION['encryption_key'] = Crypt\Random::string(32); } } return $_SESSION['encryption_key']; @@ -647,6 +634,64 @@ class AuthenticationCookie extends AuthenticationPlugin return ! class_exists('phpseclib\Crypt\Random'); } + /** + * Concatenates secret in order to make it 16 bytes log + * + * This doesn't add any security, just ensures the secret + * is long enough by copying it. + * + * @param string $secret Original secret + * + * @return string + */ + public function enlargeSecret($secret) + { + while (strlen($secret) < 16) { + $secret .= $secret; + } + return substr($secret, 0, 16); + } + + /** + * Derives MAC secret from encryption secret. + * + * @param string $secret the secret + * + * @return string the MAC secret + */ + public function getMACSecret($secret) + { + // Grab first part, up to 16 chars + // The MAC and AES secrets can overlap if original secret is short + $length = strlen($secret); + if ($length > 16) { + return substr($secret, 0, 16); + } + return $this->enlargeSecret( + $length == 1 ? $secret : substr($secret, 0, -1) + ); + } + + /** + * Derives AES secret from encryption secret. + * + * @param string $secret the secret + * + * @return string the AES secret + */ + public function getAESSecret($secret) + { + // Grab second part, up to 16 chars + // The MAC and AES secrets can overlap if original secret is short + $length = strlen($secret); + if ($length > 16) { + return substr($secret, -16); + } + return $this->enlargeSecret( + $length == 1 ? $secret : substr($secret, 1) + ); + } + /** * Encryption using openssl's AES or phpseclib's AES * (phpseclib uses mcrypt when it is available) @@ -658,20 +703,31 @@ class AuthenticationCookie extends AuthenticationPlugin */ public function cookieEncrypt($data, $secret) { + $mac_secret = $this->getMACSecret($secret); + $aes_secret = $this->getAESSecret($secret); + $iv = $this->createIV(); if (self::useOpenSSL()) { - return openssl_encrypt( + $result = openssl_encrypt( $data, 'AES-128-CBC', $secret, 0, - $this->_cookie_iv + $iv ); } else { $cipher = new Crypt\AES(Crypt\Base::MODE_CBC); - $cipher->setIV($this->_cookie_iv); - $cipher->setKey($secret); - return base64_encode($cipher->encrypt($data)); + $cipher->setIV($iv); + $cipher->setKey($aes_secret); + $result = base64_encode($cipher->encrypt($data)); } + $iv = base64_encode($iv); + return json_encode( + array( + 'iv' => $iv, + 'mac' => hash_hmac('sha1', $iv . $result, $mac_secret), + 'payload' => $result, + ) + ); } /** @@ -681,33 +737,39 @@ class AuthenticationCookie extends AuthenticationPlugin * @param string $encdata encrypted data * @param string $secret the secret * - * @return string original data + * @return string|bool original data, false on error */ public function cookieDecrypt($encdata, $secret) { - if (is_null($this->_cookie_iv)) { - $this->_cookie_iv = base64_decode( - $_COOKIE['pma_iv-' . $GLOBALS['server']], - true - ); + $data = json_decode($encdata, true); + + if (! is_array($data) || ! isset($data['mac']) || ! isset($data['iv']) || ! isset($data['payload']) + || ! is_string($data['mac']) || ! is_string($data['iv']) || ! is_string($data['payload']) + ) { + return false; } - if (strlen($this->_cookie_iv) < $this->getIVSize()) { - $this->createIV(); + + $mac_secret = $this->getMACSecret($secret); + $aes_secret = $this->getAESSecret($secret); + $newmac = hash_hmac('sha1', $data['iv'] . $data['payload'], $mac_secret); + + if (! hash_equals($data['mac'], $newmac)) { + return false; } if (self::useOpenSSL()) { return openssl_decrypt( - $encdata, + $data['payload'], 'AES-128-CBC', $secret, 0, - $this->_cookie_iv + base64_decode($data['iv']) ); } else { $cipher = new Crypt\AES(Crypt\Base::MODE_CBC); - $cipher->setIV($this->_cookie_iv); - $cipher->setKey($secret); - return $cipher->decrypt(base64_decode($encdata)); + $cipher->setIV(base64_decode($data['iv'])); + $cipher->setKey($aes_secret); + return $cipher->decrypt(base64_decode($data['payload'])); } } @@ -735,24 +797,26 @@ class AuthenticationCookie extends AuthenticationPlugin */ public function createIV() { + /* Testsuite shortcut only to allow predictable IV */ + if (! is_null($this->_cookie_iv)) { + return $this->_cookie_iv; + } if (self::useOpenSSL()) { - $this->_cookie_iv = openssl_random_pseudo_bytes( + return openssl_random_pseudo_bytes( $this->getIVSize() ); } else { - $this->_cookie_iv = Crypt\Random::string( + return Crypt\Random::string( $this->getIVSize() ); } - $GLOBALS['PMA_Config']->setCookie( - 'pma_iv-' . $GLOBALS['server'], - base64_encode($this->_cookie_iv) - ); } /** * Sets encryption IV to use * + * This is for testing only! + * * @param string $vector The IV * * @return void @@ -784,17 +848,17 @@ class AuthenticationCookie extends AuthenticationPlugin // -> delete password cookie(s) if ($GLOBALS['cfg']['LoginCookieDeleteAll']) { foreach ($GLOBALS['cfg']['Servers'] as $key => $val) { - $GLOBALS['PMA_Config']->removeCookie('pmaPass-' . $key); - if (isset($_COOKIE['pmaPass-' . $key])) { - unset($_COOKIE['pmaPass-' . $key]); + $GLOBALS['PMA_Config']->removeCookie('pmaAuth-' . $key); + if (isset($_COOKIE['pmaAuth-' . $key])) { + unset($_COOKIE['pmaAuth-' . $key]); } } } else { $GLOBALS['PMA_Config']->removeCookie( - 'pmaPass-' . $GLOBALS['server'] + 'pmaAuth-' . $GLOBALS['server'] ); - if (isset($_COOKIE['pmaPass-' . $GLOBALS['server']])) { - unset($_COOKIE['pmaPass-' . $GLOBALS['server']]); + if (isset($_COOKIE['pmaAuth-' . $GLOBALS['server']])) { + unset($_COOKIE['pmaAuth-' . $GLOBALS['server']]); } } parent::logOut(); diff --git a/libraries/plugins/export/ExportMediawiki.php b/libraries/plugins/export/ExportMediawiki.php index f74d71996a..d2d9dfb731 100644 --- a/libraries/plugins/export/ExportMediawiki.php +++ b/libraries/plugins/export/ExportMediawiki.php @@ -360,7 +360,7 @@ class ExportMediawiki extends ExportPlugin // see https://www.mediawiki.org/wiki/Help:Formatting $comment = $this->_exportCRLF(); $comment .= '' . str_repeat($this->_exportCRLF(), 2); return $comment; diff --git a/libraries/plugins/export/ExportPhparray.php b/libraries/plugins/export/ExportPhparray.php index d67902ffcf..6d4b20e13d 100644 --- a/libraries/plugins/export/ExportPhparray.php +++ b/libraries/plugins/export/ExportPhparray.php @@ -65,6 +65,19 @@ class ExportPhparray extends ExportPlugin $this->properties = $exportPluginProperties; } + /** + * Removes end of comment from a string + * + * @param string $string String to replace + * + * @return string + */ + public function commentString($string) + { + return strtr($string, '*/', '-'); + } + + /** * Outputs export header * @@ -76,7 +89,7 @@ class ExportPhparray extends ExportPlugin 'commentString(PMA\libraries\Util::backquote($db_alias)) + . $GLOBALS['crlf'] . ' */' . $GLOBALS['crlf'] ); return true; @@ -207,9 +220,9 @@ class ExportPhparray extends ExportPlugin $buffer = ''; $record_cnt = 0; // Output table name as comment - $buffer .= $crlf . '// ' - . PMA\libraries\Util::backquote($db_alias) . '.' - . PMA\libraries\Util::backquote($table_alias) . $crlf; + $buffer .= $crlf . '/* ' + . $this->commentString(PMA\libraries\Util::backquote($db_alias)) . '.' + . $this->commentString(PMA\libraries\Util::backquote($table_alias)) . ' */' . $crlf; $buffer .= '$' . $tablefixed . ' = array('; while ($record = $GLOBALS['dbi']->fetchRow($result)) { diff --git a/libraries/plugins/export/ExportSql.php b/libraries/plugins/export/ExportSql.php index f183ef2083..0fa54dfb8b 100644 --- a/libraries/plugins/export/ExportSql.php +++ b/libraries/plugins/export/ExportSql.php @@ -596,7 +596,16 @@ class ExportSql extends ExportPlugin && $GLOBALS['sql_include_comments'] ) { // see https://dev.mysql.com/doc/refman/5.0/en/ansi-diff-comments.html - return '--' . (empty($text) ? '' : ' ') . $text . $GLOBALS['crlf']; + if (empty($text)) { + return '--' . $GLOBALS['crlf']; + } else { + $lines = preg_split("/\\r\\n|\\r|\\n/", $text); + $result = array(); + foreach ($lines as $line) { + $result[] = '-- ' . $line . $GLOBALS['crlf']; + } + return implode('', $result); + } } else { return ''; } @@ -1112,7 +1121,7 @@ class ExportSql extends ExportPlugin $dbNameColumn ) . " = '" . Util::sqlAddSlashes($db) . "'" - . " AND `page_nr` = '" . $page . "'"; + . " AND `page_nr` = '" . intval($page) . "'"; if (!$this->exportData( $cfgRelation['db'], diff --git a/libraries/plugins/export/ExportXml.php b/libraries/plugins/export/ExportXml.php index b29c655db9..cf34f66e64 100644 --- a/libraries/plugins/export/ExportXml.php +++ b/libraries/plugins/export/ExportXml.php @@ -185,7 +185,7 @@ class ExportXml extends ExportPlugin if ($names) { foreach ($names as $name) { $head .= ' ' . $crlf; + . htmlspecialchars($name) . '">' . $crlf; // Do some formatting $sql = $GLOBALS['dbi']->getDefinition($db, $dbitype, $name); @@ -232,7 +232,7 @@ class ExportXml extends ExportPlugin . '- version ' . PMA_VERSION . $crlf . '- https://www.phpmyadmin.net' . $crlf . '-' . $crlf - . '- ' . __('Host:') . ' ' . $cfg['Server']['host']; + . '- ' . __('Host:') . ' ' . htmlspecialchars($cfg['Server']['host']); if (!empty($cfg['Server']['port'])) { $head .= ':' . $cfg['Server']['port']; } @@ -263,7 +263,7 @@ class ExportXml extends ExportPlugin $head .= ' -->' . $crlf; $head .= ' ' . $crlf; $head .= ' ' . $crlf; if (count($tables) == 0) { @@ -296,7 +296,7 @@ class ExportXml extends ExportPlugin continue; } - $head .= ' ' + $head .= ' ' . $crlf; $tbl = " " . htmlspecialchars($tbl); @@ -314,7 +314,7 @@ class ExportXml extends ExportPlugin foreach ($triggers as $trigger) { $code = $trigger['create']; $head .= ' ' . $crlf; + . htmlspecialchars($trigger['name']) . '">' . $crlf; // Do some formatting $code = mb_substr(rtrim($code), 0, -3); @@ -402,7 +402,7 @@ class ExportXml extends ExportPlugin ) { $head = ' ' . $crlf . ' ' . $crlf; @@ -491,7 +491,7 @@ class ExportXml extends ExportPlugin unset($i); $buffer = ' ' . $crlf; + . htmlspecialchars($table_alias) . ' -->' . $crlf; if (!PMA_exportOutputHandler($buffer)) { return false; } diff --git a/libraries/plugins/import/ImportShp.php b/libraries/plugins/import/ImportShp.php index 224472f7d4..5f25f3f52a 100644 --- a/libraries/plugins/import/ImportShp.php +++ b/libraries/plugins/import/ImportShp.php @@ -100,21 +100,24 @@ class ImportShp extends ImportPlugin // Extract the .dbf file and point to it. $extracted = PMA_zipExtract( $import_file, - realpath($GLOBALS['cfg']['TempDir']), - array($dbf_file_name) + $dbf_file_name ); - if ($extracted) { + if ($extracted !== false) { $dbf_file_path = realpath($GLOBALS['cfg']['TempDir']) - . (PMA_IS_WINDOWS ? '\\' : '/') . $dbf_file_name; - $temp_dbf_file = true; - // Replace the .dbf with .*, as required - // by the bsShapeFiles library. - $file_name = mb_substr( - $dbf_file_path, - 0, - mb_strlen($dbf_file_path) - 4 - ) . '.*'; - $shp->FileName = $file_name; + . (PMA_IS_WINDOWS ? '\\' : '/') + . PMA_sanitizeFilename($dbf_file_name, true); + $handle = fopen($dbf_file_path, 'wb'); + if ($handle !== false) { + fwrite($handle, $extracted); + fclose($handle); + $temp_dbf_file = true; + // Replace the .dbf with .*, as required + // by the bsShapeFiles library. + $file_name = substr( + $dbf_file_path, 0, strlen($dbf_file_path) - 4 + ) . '.*'; + $shp->FileName = $file_name; + } } } } elseif (!empty($local_import_file) @@ -134,6 +137,14 @@ class ImportShp extends ImportPlugin } } + // Delete the .dbf file extracted to 'TempDir' + if ($temp_dbf_file + && isset($dbf_file_path) + && file_exists($dbf_file_path) + ) { + unlink($dbf_file_path); + } + // Load data $shp->loadFromFile(''); if ($shp->lastError != "") { @@ -146,14 +157,6 @@ class ImportShp extends ImportPlugin return; } - // Delete the .dbf file extracted to 'TempDir' - if ($temp_dbf_file - && isset($dbf_file_path) - && file_exists($dbf_file_path) - ) { - unlink($dbf_file_path); - } - switch ($shp->shapeType) { // ESRI Null Shape case 0: diff --git a/libraries/plugins/schema/ExportRelationSchema.php b/libraries/plugins/schema/ExportRelationSchema.php index 96cc051370..ae203b2b28 100644 --- a/libraries/plugins/schema/ExportRelationSchema.php +++ b/libraries/plugins/schema/ExportRelationSchema.php @@ -54,7 +54,7 @@ class ExportRelationSchema */ public function setPageNumber($value) { - $this->pageNumber = $value; + $this->pageNumber = intval($value); } /** diff --git a/libraries/plugins/transformations/abs/FormattedTransformationsPlugin.php b/libraries/plugins/transformations/abs/FormattedTransformationsPlugin.php index 798dc539e4..d71842af36 100644 --- a/libraries/plugins/transformations/abs/FormattedTransformationsPlugin.php +++ b/libraries/plugins/transformations/abs/FormattedTransformationsPlugin.php @@ -42,7 +42,9 @@ abstract class FormattedTransformationsPlugin extends TransformationsPlugin */ public function applyTransformation($buffer, $options = array(), $meta = '') { - return $buffer; + return ''; } diff --git a/libraries/plugins/transformations/abs/ImageLinkTransformationsPlugin.php b/libraries/plugins/transformations/abs/ImageLinkTransformationsPlugin.php index 5246fb2e5f..429b5cfdb8 100644 --- a/libraries/plugins/transformations/abs/ImageLinkTransformationsPlugin.php +++ b/libraries/plugins/transformations/abs/ImageLinkTransformationsPlugin.php @@ -49,7 +49,7 @@ abstract class ImageLinkTransformationsPlugin extends TransformationsPlugin { // must disable the page loader, see // https://wiki.phpmyadmin.net/pma/Page_loader#Bypassing_the_page_loader - return '[BLOB]'; } diff --git a/libraries/plugins/transformations/abs/ImageUploadTransformationsPlugin.php b/libraries/plugins/transformations/abs/ImageUploadTransformationsPlugin.php index a976ff62ac..446381616f 100644 --- a/libraries/plugins/transformations/abs/ImageUploadTransformationsPlugin.php +++ b/libraries/plugins/transformations/abs/ImageUploadTransformationsPlugin.php @@ -82,8 +82,8 @@ abstract class ImageUploadTransformationsPlugin extends IOTransformationsPlugin $src = 'transformation_wrapper.php' . $options['wrapper_link']; } $html .= ''
+            . (isset($options[0]) ? intval($options[0]) : '100') . ''; $html .= '
'; diff --git a/libraries/plugins/transformations/abs/InlineTransformationsPlugin.php b/libraries/plugins/transformations/abs/InlineTransformationsPlugin.php index 9d2a12f59f..ac9134a3de 100644 --- a/libraries/plugins/transformations/abs/InlineTransformationsPlugin.php +++ b/libraries/plugins/transformations/abs/InlineTransformationsPlugin.php @@ -51,10 +51,10 @@ abstract class InlineTransformationsPlugin extends TransformationsPlugin if (PMA_IS_GD2) { return '[' . htmlspecialchars($buffer) . ']'; } else { return '' + . '" rel="noopener noreferrer" target="_blank">' . htmlspecialchars($buffer) . ''; } diff --git a/libraries/plugins/transformations/abs/TextLinkTransformationsPlugin.php b/libraries/plugins/transformations/abs/TextLinkTransformationsPlugin.php index 9f160906ee..c1a09810be 100644 --- a/libraries/plugins/transformations/abs/TextLinkTransformationsPlugin.php +++ b/libraries/plugins/transformations/abs/TextLinkTransformationsPlugin.php @@ -49,14 +49,14 @@ abstract class TextLinkTransformationsPlugin extends TransformationsPlugin $url = (isset($options[0]) ? $options[0] : '') . ((isset($options[2]) && $options[2]) ? '' : $buffer); $parsed = parse_url($url); /* Do not allow javascript links */ - if (isset($parsed['scheme']) && $parsed['scheme'] == 'javascript') { + if (! isset($parsed['scheme']) || ! in_array(strtolower($parsed['scheme']), array('http', 'https', 'ftp', 'mailto'))) { return htmlspecialchars($url); } return '' + . '" target="_blank" rel="noopener noreferrer">' . htmlspecialchars(isset($options[1]) ? $options[1] : $buffer) . ''; } diff --git a/libraries/pmd_common.php b/libraries/pmd_common.php index 5b84015bf1..03e018c5cc 100644 --- a/libraries/pmd_common.php +++ b/libraries/pmd_common.php @@ -261,7 +261,7 @@ function PMA_getTablePositions($pg) 1 AS `H` FROM " . PMA\libraries\Util::backquote($cfgRelation['db']) . "." . PMA\libraries\Util::backquote($cfgRelation['table_coords']) . " - WHERE pdf_page_number = " . $pg; + WHERE pdf_page_number = " . intval($pg); $tab_pos = $GLOBALS['dbi']->fetchResult( $query, @@ -290,7 +290,7 @@ function PMA_getPageName($pg) $query = "SELECT `page_descr`" . " FROM " . PMA\libraries\Util::backquote($cfgRelation['db']) . "." . PMA\libraries\Util::backquote($cfgRelation['pdf_pages']) - . " WHERE " . PMA\libraries\Util::backquote('page_nr') . " = " . $pg; + . " WHERE " . PMA\libraries\Util::backquote('page_nr') . " = " . intval($pg); $page_name = $GLOBALS['dbi']->fetchResult( $query, null, @@ -317,7 +317,7 @@ function PMA_deletePage($pg) $query = "DELETE FROM " . PMA\libraries\Util::backquote($cfgRelation['db']) . "." . PMA\libraries\Util::backquote($cfgRelation['table_coords']) - . " WHERE " . PMA\libraries\Util::backquote('pdf_page_number') . " = " . $pg; + . " WHERE " . PMA\libraries\Util::backquote('pdf_page_number') . " = " . intval($pg); $success = PMA_queryAsControlUser( $query, true, PMA\libraries\DatabaseInterface::QUERY_STORE ); @@ -325,7 +325,7 @@ function PMA_deletePage($pg) if ($success) { $query = "DELETE FROM " . PMA\libraries\Util::backquote($cfgRelation['db']) . "." . PMA\libraries\Util::backquote($cfgRelation['pdf_pages']) - . " WHERE " . PMA\libraries\Util::backquote('page_nr') . " = " . $pg; + . " WHERE " . PMA\libraries\Util::backquote('page_nr') . " = " . intval($pg); $success = PMA_queryAsControlUser( $query, true, PMA\libraries\DatabaseInterface::QUERY_STORE ); @@ -364,7 +364,7 @@ function PMA_getDefaultPage($db) ); if (count($default_page_no)) { - return $default_page_no[0]; + return intval($default_page_no[0]); } return -1; } @@ -406,7 +406,7 @@ function PMA_getLoadingPage($db) $page_no = $min_page_no[0]; } } - return $page_no; + return intval($page_no); } /** diff --git a/libraries/relation.lib.php b/libraries/relation.lib.php index c4f81e03e3..6d05731aaa 100644 --- a/libraries/relation.lib.php +++ b/libraries/relation.lib.php @@ -1666,7 +1666,7 @@ function PMA_REL_renameTable($source_db, $target_db, $source_table, $target_tabl * @param array $cfgRelation Relation configuration * @param string $db database name * - * @return string $pdf_page_number + * @return int $pdf_page_number */ function PMA_REL_createPage($newpage, $cfgRelation, $db) { diff --git a/libraries/replication.inc.php b/libraries/replication.inc.php index b34f520122..663388560d 100644 --- a/libraries/replication.inc.php +++ b/libraries/replication.inc.php @@ -295,7 +295,7 @@ function PMA_Replication_connectToMaster( $server = array(); $server['user'] = $user; $server['password'] = $password; - $server["host"] = $host; + $server["host"] = PMA_sanitizeMySQLHost($host); $server["port"] = $port; $server["socket"] = $socket; diff --git a/libraries/replication_gui.lib.php b/libraries/replication_gui.lib.php index cc259ceed9..70c7ffb6aa 100644 --- a/libraries/replication_gui.lib.php +++ b/libraries/replication_gui.lib.php @@ -549,7 +549,7 @@ function PMA_getHtmlForReplicationStatusTable($type, $hidden = false, $title = t foreach (${"{$type}_variables"} as $variable) { $html .= ' '; $html .= ' '; - $html .= $variable; + $html .= htmlspecialchars($variable); $html .= ' '; $html .= ' '; @@ -572,13 +572,13 @@ function PMA_getHtmlForReplicationStatusTable($type, $hidden = false, $title = t 'Replicate_Do_Table', 'Replicate_Ignore_Table', 'Replicate_Wild_Do_Table', 'Replicate_Wild_Ignore_Table'); if (in_array($variable, $variables_wrap)) { - $html .= str_replace( + $html .= htmlspecialchars(str_replace( ',', ', ', ${"server_{$type}_replication"}[0][$variable] - ); + )); } else { - $html .= ${"server_{$type}_replication"}[0][$variable]; + $html .= htmlspecialchars(${"server_{$type}_replication"}[0][$variable]); } $html .= ''; @@ -807,7 +807,7 @@ function PMA_getHtmlForAddUserLoginForm($username_length) . (empty($_REQUEST['username']) ? '' : ' value="' . (isset($GLOBALS['new_username']) ? $GLOBALS['new_username'] - : $_REQUEST['username']) . '"') + : htmlspecialchars($_REQUEST['username'])) . '"') . ' />' . ''; @@ -837,7 +837,7 @@ function PMA_getHtmlForTableInfoForm($hostname_length) . '' . '' . PMA\libraries\Util::showHint( diff --git a/libraries/server_privileges.lib.php b/libraries/server_privileges.lib.php index a4098f7365..3713c5143c 100644 --- a/libraries/server_privileges.lib.php +++ b/libraries/server_privileges.lib.php @@ -2390,13 +2390,13 @@ function PMA_getHtmlTableBodyForSpecificDbRoutinePrivs($db, $odd_row, $index_che . 'id="checkbox_sel_users_' . ($index_checkbox++) . '" ' . 'value="' . $value . '" />'; - $html_output .= '' . $row['User'] + $html_output .= '' . htmlspecialchars($row['User']) . '' - . '' . $row['Host'] + . '' . htmlspecialchars($row['Host']) . '' . '' . 'routine' . '' - . '' . '' . $row['Routine_name'] . '' + . '' . '' . htmlspecialchars($row['Routine_name']) . '' . '' . '' . 'Yes' . ''; diff --git a/libraries/server_status_variables.lib.php b/libraries/server_status_variables.lib.php index b44a5c7205..60ec7d589e 100644 --- a/libraries/server_status_variables.lib.php +++ b/libraries/server_status_variables.lib.php @@ -285,7 +285,7 @@ function PMA_getHtmlForRenderVariables($ServerStatusData, $alerts, $strShowStatu $retval .= ''; } } - $retval .= $value; + $retval .= htmlspecialchars($value); if (isset($alerts[$name])) { $retval .= ''; } diff --git a/libraries/server_user_groups.lib.php b/libraries/server_user_groups.lib.php index 80370745e3..711f71d98b 100644 --- a/libraries/server_user_groups.lib.php +++ b/libraries/server_user_groups.lib.php @@ -352,7 +352,7 @@ function PMA_editUserGroup($userGroup, $new = false) } $tabName = $tabGroupName . '_' . $tab; $allowed = isset($_REQUEST[$tabName]) && $_REQUEST[$tabName] == 'Y'; - $sql_query .= "('" . $userGroup . "', '" . $tabName . "', '" + $sql_query .= "('" . PMA_Util::sqlAddSlashes($userGroup) . "', '" . $tabName . "', '" . ($allowed ? "Y" : "N") . "')"; $first = false; } diff --git a/libraries/session.inc.php b/libraries/session.inc.php index 32667d29a4..fba46aadb0 100644 --- a/libraries/session.inc.php +++ b/libraries/session.inc.php @@ -75,7 +75,23 @@ function PMA_sessionFailed($errors) { $messages = array(); foreach ($errors as $error) { - $messages[] = $error->getMessage(); + /* + * Remove path from open() in error message to avoid path disclossure + * + * This can happen with PHP 5 when nonexisting session ID is provided, + * since PHP 7, session existence is checked first. + * + * This error can also happen in case of session backed error (eg. + * read only filesystem) on any PHP version. + * + * The message string is currently hardcoded in PHP, so hopefully it + * will not change in future. + */ + $messages[] = preg_replace( + '/open(.*, O_RDWR)/', + 'open(SESSION_FILE, O_RDWR)', + $error->getMessage() + ); } /* diff --git a/libraries/tracking.lib.php b/libraries/tracking.lib.php index 785c6fa8f1..c447f41683 100644 --- a/libraries/tracking.lib.php +++ b/libraries/tracking.lib.php @@ -393,9 +393,7 @@ function PMA_getHtmlForTableVersionDetails( function PMA_getTableLastVersionNumber($sql_result) { $maxversion = $GLOBALS['dbi']->fetchArray($sql_result); - $last_version = $maxversion['version']; - - return $last_version; + return intval($maxversion['version']); } /** @@ -867,7 +865,10 @@ function PMA_getHtmlForSchemaSnapshot($url_query) ); // Unserialize snapshot - $temp = unserialize($data['schema_snapshot']); + $temp = PMA_safeUnserialize($data['schema_snapshot']); + if ($temp === null) { + $temp = array('COLUMNS' => array(), 'INDEXES' => array()); + } $columns = $temp['COLUMNS']; $indexes = $temp['INDEXES']; $html .= PMA_getHtmlForColumns($columns); diff --git a/libraries/zip_extension.lib.php b/libraries/zip_extension.lib.php index f67c8a8a8d..7eda57365c 100644 --- a/libraries/zip_extension.lib.php +++ b/libraries/zip_extension.lib.php @@ -143,19 +143,18 @@ function PMA_getNoOfFilesInZip($file) /** * Extracts a set of files from the given zip archive to a given destinations. * - * @param string $zip_path path to the zip archive - * @param string $destination destination to extract files - * @param array $entries files in archive that should be extracted + * @param string $zip_path path to the zip archive + * @param string $entry file in the archive that should be extracted * - * @return bool true on success, false otherwise + * @return string|bool data on sucess, false otherwise */ -function PMA_zipExtract($zip_path, $destination, $entries) +function PMA_zipExtract($zip_path, $entry) { $zip = new ZipArchive; if ($zip->open($zip_path) === true) { - $zip->extractTo($destination, $entries); + $result = $zip->getFromName($entry); $zip->close(); - return true; + return $result; } return false; } diff --git a/lint.php b/lint.php index e1d6b5f7b8..25ee88b132 100644 --- a/lint.php +++ b/lint.php @@ -7,6 +7,8 @@ */ use PMA\libraries\Linter; +$_GET['ajax_request'] = 'true'; + /** * Loading common files. Used to check for authorization, localization and to * load the parsing library. diff --git a/normalization.php b/normalization.php index e3598e17ca..4c5dbf615b 100644 --- a/normalization.php +++ b/normalization.php @@ -27,7 +27,7 @@ if (isset($_REQUEST['getColumns'])) { exit; } if (isset($_REQUEST['splitColumn'])) { - $num_fields = $_REQUEST['numFields']; + $num_fields = min(4096, intval($_REQUEST['numFields'])); $html = PMA_getHtmlForCreateNewColumn($num_fields, $db, $table); $html .= URL::getHiddenInputs($db, $table); echo $html; diff --git a/phpinfo.php b/phpinfo.php deleted file mode 100644 index 55ff9d27b4..0000000000 --- a/phpinfo.php +++ /dev/null @@ -1,20 +0,0 @@ -disable(); - -/** - * Displays PHP information - */ -if ($GLOBALS['cfg']['ShowPhpInfo']) { - phpinfo(); -} diff --git a/libraries/plugins/transformations/generator_main_class.sh b/scripts/transformations_generator_main_class.sh similarity index 100% rename from libraries/plugins/transformations/generator_main_class.sh rename to scripts/transformations_generator_main_class.sh diff --git a/libraries/plugins/transformations/generator_plugin.sh b/scripts/transformations_generator_plugin.sh similarity index 100% rename from libraries/plugins/transformations/generator_plugin.sh rename to scripts/transformations_generator_plugin.sh diff --git a/tbl_addfield.php b/tbl_addfield.php index 2ec8a2972b..2996d147ab 100644 --- a/tbl_addfield.php +++ b/tbl_addfield.php @@ -43,10 +43,13 @@ if (isset($_REQUEST['submit_num_fields'])) { if (isset($_REQUEST['orig_field_where'])) { $_REQUEST['field_where'] = $_REQUEST['orig_field_where']; } - $num_fields = $_REQUEST['orig_num_fields'] + $_REQUEST['added_fields']; + $num_fields = min( + intval($_REQUEST['orig_num_fields']) + intval($_REQUEST['added_fields']), + 4096 + ); $regenerate = true; } elseif (isset($_REQUEST['num_fields']) && intval($_REQUEST['num_fields']) > 0) { - $num_fields = (int) $_REQUEST['num_fields']; + $num_fields = min(4096, intval($_REQUEST['num_fields'])); } else { $num_fields = 1; } diff --git a/templates/list/item.phtml b/templates/list/item.phtml index 099a211d86..c3efdde8e0 100644 --- a/templates/list/item.phtml +++ b/templates/list/item.phtml @@ -21,6 +21,10 @@ ? ' target="' . $url['target'] . '"' : null; ?> + - diff --git a/templates/table/gis_visualization/gis_visualization.phtml b/templates/table/gis_visualization/gis_visualization.phtml index 1ffc16c0bf..e2b92ae386 100644 --- a/templates/table/gis_visualization/gis_visualization.phtml +++ b/templates/table/gis_visualization/gis_visualization.phtml @@ -10,8 +10,8 @@ @@ -20,8 +20,8 @@ diff --git a/templates/table/search/zoom_result_form.phtml b/templates/table/search/zoom_result_form.phtml index 5e17501150..91f89b2990 100644 --- a/templates/table/search/zoom_result_form.phtml +++ b/templates/table/search/zoom_result_form.phtml @@ -16,7 +16,7 @@