diff --git a/core/INSTALL.txt b/core/INSTALL.txt index ab25c46af550f6daedeaa225369dc279ef3c1853..079533c7b6ab8387f556b370fa27578ace853bd5 100644 --- a/core/INSTALL.txt +++ b/core/INSTALL.txt @@ -18,7 +18,8 @@ Drupal requires: - A web server with PHP support, for example: - Apache 2.0 (or greater) (http://httpd.apache.org/). - Nginx 1.1 (or greater) (http://nginx.com/). -- PHP 5.4.5 (or greater) (http://php.net/). +- PHP 5.4.5 (or greater) (http://php.net/). For better security support its + recommended to update to at least 5.5.21 or 5.6.5. - One of the following databases: - MySQL 5.0.15 (or greater) (http://www.mysql.com/). - MariaDB 5.1.44 (or greater) (https://mariadb.org/). MariaDB is a fully @@ -62,6 +63,10 @@ OPTIONAL SERVER REQUIREMENTS configuration allows the web server to initiate outbound connections. Most web hosting setups allow this. +- PHP 5.5.21 provides features for improved security when used with MySQL. While + this is not required, it is highly encouraged to use PHP 5.5.21 or 5.6.5 and + above. + INSTALLATION ------------ diff --git a/core/includes/install.inc b/core/includes/install.inc index 61d286bdec821373b79e9343463fb0c7b00114b5..7e0e32e65cad76544836a70798d3e183a0a7307a 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -925,6 +925,8 @@ function drupal_check_profile($profile, array $install_state) { foreach ($info['dependencies'] as $module) { module_load_install($module); $function = $module . '_requirements'; + + drupal_classloader_register($module, drupal_get_path('module', $module)); if (function_exists($function)) { $requirements = array_merge($requirements, $function('install')); } diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php index 8a70fb0cf2d46705d9a3af9bf32b735705f15b28..f31389262d70a92ae3e4894c75c9ea40d385ae90 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php @@ -83,6 +83,11 @@ public static function open(array &$connection_options = array()) { // Because MySQL's prepared statements skip the query cache, because it's dumb. \PDO::ATTR_EMULATE_PREPARES => TRUE, ); + if (defined('\PDO::MYSQL_ATTR_MULTI_STATEMENTS')) { + // An added connection option in PHP 5.5.21 to optionally limit SQL to a + // single statement like mysqli. + $connection_options['pdo'] += [\PDO::MYSQL_ATTR_MULTI_STATEMENTS => FALSE]; + } $pdo = new \PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']); @@ -110,8 +115,10 @@ public static function open(array &$connection_options = array()) { $connection_options['init_commands'] += array( 'sql_mode' => "SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'", ); - // Set connection options. - $pdo->exec(implode('; ', $connection_options['init_commands'])); + // Execute initial commands. + foreach ($connection_options['init_commands'] as $sql) { + $pdo->exec($sql); + } return $pdo; } diff --git a/core/modules/system/src/SystemRequirements.php b/core/modules/system/src/SystemRequirements.php new file mode 100644 index 0000000000000000000000000000000000000000..ecaf0cf4b83cc97d16ff18882db94a23688774ec --- /dev/null +++ b/core/modules/system/src/SystemRequirements.php @@ -0,0 +1,29 @@ +<?php + +/** + * @file + * Contains \Drupal\system\SystemRequirements. + */ + +namespace Drupal\system; + +/** + * Class for helper methods used for the system requirements. + */ +class SystemRequirements { + + /** + * Determines whether the passed in PHP version disallows multiple statements. + * + * @param string $phpversion + * + * @return bool + */ + public static function phpVersionWithPdoDisallowMultipleStatements($phpversion) { + // PDO::MYSQL_ATTR_MULTI_STATEMENTS was introduced in PHP versions 5.5.21 + // and 5.6.5. + return (version_compare($phpversion, '5.5.21', '>=') && version_compare($phpversion, '5.6.0', '<')) + || version_compare($phpversion, '5.6.5', '>='); + } + +} diff --git a/core/modules/system/src/Tests/Database/ConnectionTest.php b/core/modules/system/src/Tests/Database/ConnectionTest.php index c0d9253a17669b11bcddb2b2fa165536fde864aa..d6fe5f9ca25e719c39f52efb3af001738d9fe08d 100644 --- a/core/modules/system/src/Tests/Database/ConnectionTest.php +++ b/core/modules/system/src/Tests/Database/ConnectionTest.php @@ -118,4 +118,22 @@ function testConnectionOptions() { $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.'); } + /** + * Ensure that you cannot execute multiple statements on phpversion() > 5.5.21 or > 5.6.5. + */ + public function testMultipleStatementsForNewPhp() { + if (!defined('\PDO::MYSQL_ATTR_MULTI_STATEMENTS')) { + return; + } + + $db = Database::getConnection('default', 'default'); + try { + $db->query('SELECT * FROM {test}; SELECT * FROM {test_people}')->execute(); + $this->fail('NO PDO exception thrown for multiple statements.'); + } + catch (\Exception $e) { + $this->pass('PDO exception thrown for multiple statements.'); + } + } + } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 94c8e33b6fb7accde4234264724d7c22bbb2ae2c..8355a2c52a28a3a46d93655b4cdd7c9a0b26a1df 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -14,6 +14,7 @@ use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\PrivateStream; use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\system\SystemRequirements; /** * Implements hook_requirements(). @@ -80,6 +81,17 @@ function system_requirements($phase) { return $requirements; } + // Suggest to update to at least 5.5.21 or 5.6.5 for disabling multiple + // statements. + if (($phase === 'install' || \Drupal::database()->driver() === 'mysql') && !SystemRequirements::phpVersionWithPdoDisallowMultipleStatements($phpversion)) { + $requirements['php'] = array( + 'title' => t('PHP (multiple statement disabling)'), + 'value' => $phpversion, + 'description' => t('PHP versions higher than 5.6.5 or 5.5.21 provide built-in SQL injection protection for mysql databases. It is recommended to update.'), + 'severity' => REQUIREMENT_INFO, + ); + } + // Test for PHP extensions. $requirements['php_extensions'] = array( 'title' => t('PHP extensions'), diff --git a/core/modules/system/tests/src/Unit/SystemRequirementsTest.php b/core/modules/system/tests/src/Unit/SystemRequirementsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8d547ea3cf7bf463d267869449748e15991db0e0 --- /dev/null +++ b/core/modules/system/tests/src/Unit/SystemRequirementsTest.php @@ -0,0 +1,39 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\system\Unit\SystemRequirementsTest. + */ + +namespace Drupal\Tests\system\Unit; + +use Drupal\system\SystemRequirements; +use Drupal\Tests\UnitTestCase; + +/** + * @coversDefaultClass Drupal\system\SystemRequirements + */ +class SystemRequirementsTest extends UnitTestCase { + + /** + * @dataProvider providerTestPhpVersionWithPdoDisallowMultipleStatements + */ + public function testPhpVersionWithPdoDisallowMultipleStatements($version, $expected) { + $this->assertEquals($expected, SystemRequirements::phpVersionWithPdoDisallowMultipleStatements($version)); + } + + public function providerTestPhpVersionWithPdoDisallowMultipleStatements() { + $data = []; + $data[] = ['5.4.2', FALSE]; + $data[] = ['5.4.21', FALSE]; + $data[] = ['5.5.9', FALSE]; + $data[] = ['5.5.20', FALSE]; + $data[] = ['5.5.21', TRUE]; + $data[] = ['5.5.30', TRUE]; + $data[] = ['5.6.2', FALSE]; + $data[] = ['5.6.5', TRUE]; + $data[] = ['5.5.21', TRUE]; + return $data; + } + +}