diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php index 7cbe16706afcd9f3d81b35d59775e948afeb1bbb..e0c1714149bcb42292a55f25383460be0085c30b 100644 --- a/core/lib/Drupal/Core/Database/Connection.php +++ b/core/lib/Drupal/Core/Database/Connection.php @@ -596,7 +596,7 @@ protected function expandArguments(&$query, &$args) { // to expand it out into a comma-delimited set of placeholders. foreach (array_filter($args, 'is_array') as $key => $data) { $new_keys = array(); - foreach ($data as $i => $value) { + foreach (array_values($data) as $i => $value) { // This assumes that there are no other placeholders that use the same // name. For example, if the array placeholder is defined as :example // and there is already an :example_2 placeholder, this will generate diff --git a/core/modules/system/src/Tests/Database/QueryTest.php b/core/modules/system/src/Tests/Database/QueryTest.php index ddfd1a5818473e8e3de3170bda859a9bf2f5c8a8..964253eddf31c41201e70cf666dc6579b39b1697 100644 --- a/core/modules/system/src/Tests/Database/QueryTest.php +++ b/core/modules/system/src/Tests/Database/QueryTest.php @@ -7,6 +7,8 @@ namespace Drupal\system\Tests\Database; +use Drupal\Core\Database\DatabaseExceptionWrapper; + /** * Tests Drupal's extended prepared statement syntax.. * @@ -21,4 +23,32 @@ function testArraySubstitution() { $this->assertEqual(count($names), 3, 'Correct number of names returned'); } + + /** + * Tests SQL injection via database query array arguments. + */ + public function testArrayArgumentsSQLInjection() { + // Attempt SQL injection and verify that it does not work. + $condition = array( + "1 ;INSERT INTO {test} SET name = 'test12345678'; -- " => '', + '1' => '', + ); + try { + db_query("SELECT * FROM {test} WHERE name = :name", array(':name' => $condition))->fetchObject(); + $this->fail('SQL injection attempt via array arguments should result in a database exception.'); + } + catch (DatabaseExceptionWrapper $e) { + $this->pass('SQL injection attempt via array arguments should result in a database exception.'); + } + + // Test that the insert query that was used in the SQL injection attempt did + // not result in a row being inserted in the database. + $result = db_select('test') + ->condition('name', 'test12345678') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.'); + } + }