diff --git a/libraries/classes/Core.php b/libraries/classes/Core.php index 447b951daa..be8b21ad86 100644 --- a/libraries/classes/Core.php +++ b/libraries/classes/Core.php @@ -1289,4 +1289,32 @@ class Core self::fatalError(__('possible exploit')); } } + + /** + * Sign the sql query using hmac using the session token + * + * @param string $sqlQuery The sql query + * @return void + */ + public static function signSqlQuery($sqlQuery) + { + /** @var array $cfg */ + global $cfg; + return hash_hmac('sha256', $sqlQuery, $_SESSION[' HMAC_secret '] . $cfg['blowfish_secret']); + } + + /** + * Check that the sql query has a valid hmac signature + * + * @param string $sqlQuery The sql query + * @return void + */ + public static function checkSqlQuerySignature($sqlQuery, $signature) + { + /** @var array $cfg */ + global $cfg; + $hmac = hash_hmac('sha256', $sqlQuery, $_SESSION[' HMAC_secret '] . $cfg['blowfish_secret']); + return hash_equals($hmac, $signature); + } + } diff --git a/libraries/classes/Display/Results.php b/libraries/classes/Display/Results.php index 87d0879b5f..cde874aee5 100644 --- a/libraries/classes/Display/Results.php +++ b/libraries/classes/Display/Results.php @@ -5313,16 +5313,19 @@ class Results $title = htmlspecialchars($data); } + $sqlQuery = 'SELECT * FROM ' + . Util::backquote($map[$meta->name][3]) . '.' + . Util::backquote($map[$meta->name][0]) + . ' WHERE ' + . Util::backquote($map[$meta->name][1]) + . $where_comparison; + $_url_params = array( 'db' => $map[$meta->name][3], 'table' => $map[$meta->name][0], 'pos' => '0', - 'sql_query' => 'SELECT * FROM ' - . Util::backquote($map[$meta->name][3]) . '.' - . Util::backquote($map[$meta->name][0]) - . ' WHERE ' - . Util::backquote($map[$meta->name][1]) - . $where_comparison, + 'sql_signature' => Core::signSqlQuery($sqlQuery), + 'sql_query' => $sqlQuery, ); if ($transformation_plugin != $default_function) { diff --git a/libraries/classes/Plugins/Auth/AuthenticationSignon.php b/libraries/classes/Plugins/Auth/AuthenticationSignon.php index 828727eac5..a6d1c7bf15 100644 --- a/libraries/classes/Plugins/Auth/AuthenticationSignon.php +++ b/libraries/classes/Plugins/Auth/AuthenticationSignon.php @@ -214,6 +214,7 @@ class AuthenticationSignon extends AuthenticationPlugin /* Restore our token */ if (!empty($pma_token)) { $_SESSION[' PMA_token '] = $pma_token; + $_SESSION[' HMAC_secret '] = Util::generateRandom(16); } /** diff --git a/libraries/classes/Session.php b/libraries/classes/Session.php index 45799d4bb9..d05bbc7a41 100644 --- a/libraries/classes/Session.php +++ b/libraries/classes/Session.php @@ -29,6 +29,7 @@ class Session private static function generateToken() { $_SESSION[' PMA_token '] = Util::generateRandom(16); + $_SESSION[' HMAC_secret '] = Util::generateRandom(16); /** * Check if token is properly generated (the generation can fail, for example diff --git a/libraries/classes/Util.php b/libraries/classes/Util.php index a3f08ad4ec..c64a709444 100644 --- a/libraries/classes/Util.php +++ b/libraries/classes/Util.php @@ -1760,7 +1760,8 @@ class Util $tag_params_strings = array(); if (($url_length > $GLOBALS['cfg']['LinkLengthLimit']) || ! $in_suhosin_limits - || strpos($url, 'sql_query=') !== false + // Has as sql_query without a signature + || ( strpos($url, 'sql_query=') !== false && strpos($url, 'sql_signature=') === false) || strpos($url, 'view[as]=') !== false ) { $parts = explode('?', $url, 2); diff --git a/sql.php b/sql.php index 5e73353519..8a5c2cd755 100644 --- a/sql.php +++ b/sql.php @@ -13,6 +13,7 @@ use PhpMyAdmin\Response; use PhpMyAdmin\Sql; use PhpMyAdmin\Url; use PhpMyAdmin\Util; +use PhpMyAdmin\Core; /** * Gets some core libraries @@ -71,6 +72,10 @@ if (isset($_POST['bkm_fields']['bkm_sql_query'])) { $sql_query = $_POST['bkm_fields']['bkm_sql_query']; } elseif (isset($_POST['sql_query'])) { $sql_query = $_POST['sql_query']; +} elseif (isset($_GET['sql_query']) && isset($_GET['sql_signature'])) { + if (Core::checkSqlQuerySignature($_GET['sql_query'], $_GET['sql_signature'])) { + $sql_query = $_GET['sql_query']; + } } // This one is just to fill $db diff --git a/test/classes/CoreTest.php b/test/classes/CoreTest.php index ddda7b7010..26b1de9a88 100644 --- a/test/classes/CoreTest.php +++ b/test/classes/CoreTest.php @@ -1121,4 +1121,110 @@ class CoreTest extends PmaTestCase $this->assertGreaterThan(0, mb_strpos($printed, $warn)); } + + /** + * Test for Core::signSqlQuery + * + * @return void + */ + function testSignSqlQuery() + { + $_SESSION[' HMAC_secret '] = hash('sha1', 'test'); + $sqlQuery = 'SELECT * FROM `test`.`db` WHERE 1;'; + $signature = Core::signSqlQuery($sqlQuery); + $hmac = '33371e8680a640dc05944a2a24e6e630d3e9e3dba24464135f2fb954c3a4ffe2'; + $this->assertSame($hmac, $signature, 'The signature must match the computed one'); + } + + /** + * Test for Core::checkSqlQuerySignature + * + * @return void + */ + function testCheckSqlQuerySignature() + { + $_SESSION[' HMAC_secret '] = hash('sha1', 'test'); + $sqlQuery = 'SELECT * FROM `test`.`db` WHERE 1;'; + $hmac = '33371e8680a640dc05944a2a24e6e630d3e9e3dba24464135f2fb954c3a4ffe2'; + $this->assertTrue(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + } + + /** + * Test for Core::checkSqlQuerySignature + * + * @return void + */ + function testCheckSqlQuerySignatureFails() + { + $_SESSION[' HMAC_secret '] = hash('sha1', '132654987gguieunofz'); + $sqlQuery = 'SELECT * FROM `test`.`db` WHERE 1;'; + $hmac = '33371e8680a640dc05944a2a24e6e630d3e9e3dba24464135f2fb954c3a4ffe2'; + $this->assertFalse(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + } + + /** + * Test for Core::checkSqlQuerySignature + * + * @return void + */ + function testCheckSqlQuerySignatureFailsBadHash() + { + $_SESSION[' HMAC_secret '] = hash('sha1', 'test'); + $sqlQuery = 'SELECT * FROM `test`.`db` WHERE 1;'; + $hmac = '3333333380a640dc05944a2a24e6e630d3e9e3dba24464135f2fb954c3eeeeee'; + $this->assertFalse(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + } + + /** + * Test for Core::checkSqlQuerySignature + * + * @return void + */ + function testCheckSqlQuerySignatureFailsNoSession() + { + $_SESSION[' HMAC_secret '] = 'empty'; + $sqlQuery = 'SELECT * FROM `test`.`db` WHERE 1;'; + $hmac = '3333333380a640dc05944a2a24e6e630d3e9e3dba24464135f2fb954c3eeeeee'; + $this->assertFalse(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + } + + /** + * Test for Core::checkSqlQuerySignature + * + * @return void + */ + function testCheckSqlQuerySignatureFailsFromAnotherSession() + { + $_SESSION[' HMAC_secret '] = hash('sha1', 'firstSession'); + $sqlQuery = 'SELECT * FROM `test`.`db` WHERE 1;'; + $hmac = Core::signSqlQuery($sqlQuery); + $this->assertTrue(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + $_SESSION[' HMAC_secret '] = hash('sha1', 'secondSession'); + // Try to use the token (hmac) from the previous session + $this->assertFalse(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + } + + /** + * Test for Core::checkSqlQuerySignature + * + * @return void + */ + function testCheckSqlQuerySignatureFailsBlowfishSecretChanged() + { + $GLOBALS['cfg']['blowfish_secret'] = ''; + $_SESSION[' HMAC_secret '] = hash('sha1', 'firstSession'); + $sqlQuery = 'SELECT * FROM `test`.`db` WHERE 1;'; + $hmac = Core::signSqlQuery($sqlQuery); + $this->assertTrue(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + $GLOBALS['cfg']['blowfish_secret'] = '32154987zd'; + // Try to use the previous HMAC signature + $this->assertFalse(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + + $GLOBALS['cfg']['blowfish_secret'] = '32154987zd'; + // Generate the HMAC signature to check that it works + $hmac = Core::signSqlQuery($sqlQuery); + // Must work now, (good secret and blowfish_secret) + $this->assertTrue(Core::checkSqlQuerySignature($sqlQuery, $hmac)); + } + }