Same name and namespace in other branches
  1. 8.9.x core/tests/Drupal/KernelTests/Core/Database/QueryTest.php \Drupal\KernelTests\Core\Database\QueryTest::testConditionOperatorArgumentsSQLInjection()
  2. 9 core/tests/Drupal/KernelTests/Core/Database/QueryTest.php \Drupal\KernelTests\Core\Database\QueryTest::testConditionOperatorArgumentsSQLInjection()

Tests SQL injection via condition operator.

File

core/tests/Drupal/KernelTests/Core/Database/QueryTest.php, line 74

Class

QueryTest
Tests Drupal's extended prepared statement syntax..

Namespace

Drupal\KernelTests\Core\Database

Code

public function testConditionOperatorArgumentsSQLInjection() {
  $injection = "IS NOT NULL) ;INSERT INTO {test} (name) VALUES ('test12345678'); -- ";
  $previous_error_handler = set_error_handler(function ($severity, $message, $filename, $lineno) use (&$previous_error_handler) {

    // Normalize the filename to use UNIX directory separators.
    if (preg_match('@core/lib/Drupal/Core/Database/Query/Condition.php$@', str_replace(DIRECTORY_SEPARATOR, '/', $filename))) {

      // Convert errors to exceptions for testing purposes below.
      throw new \ErrorException($message, 0, $severity, $filename, $lineno);
    }
    if ($previous_error_handler) {
      return $previous_error_handler($severity, $message, $filename, $lineno);
    }
  });
  try {
    $result = $this->connection
      ->select('test', 't')
      ->fields('t')
      ->condition('name', 1, $injection)
      ->execute();
    $this
      ->fail('Should not be able to attempt SQL injection via condition operator.');
  } catch (\ErrorException $e) {

    // Expected exception; just continue testing.
  }

  // 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 = $this->connection
    ->select('test')
    ->condition('name', 'test12345678')
    ->countQuery()
    ->execute()
    ->fetchField();
  $this
    ->assertEquals(0, $result, 'SQL injection attempt did not result in a row being inserted in the database table.');

  // Attempt SQLi via union query with no unsafe characters.
  $this
    ->enableModules([
    'user',
  ]);
  $this
    ->installEntitySchema('user');
  $this->connection
    ->insert('test')
    ->fields([
    'name' => '123456',
  ])
    ->execute();
  $injection = "= 1 UNION ALL SELECT password FROM user WHERE uid =";
  try {
    $result = $this->connection
      ->select('test', 't')
      ->fields('t', [
      'name',
      'name',
    ])
      ->condition('name', 1, $injection)
      ->execute();
    $this
      ->fail('Should not be able to attempt SQL injection via operator.');
  } catch (\ErrorException $e) {

    // Expected exception; just continue testing.
  }

  // Attempt SQLi via union query - uppercase tablename.
  $this->connection
    ->insert('TEST_UPPERCASE')
    ->fields([
    'name' => 'secrets',
  ])
    ->execute();
  $injection = "IS NOT NULL) UNION ALL SELECT name FROM {TEST_UPPERCASE} -- ";
  try {
    $result = $this->connection
      ->select('test', 't')
      ->fields('t', [
      'name',
    ])
      ->condition('name', 1, $injection)
      ->execute();
    $this
      ->fail('Should not be able to attempt SQL injection via operator.');
  } catch (\ErrorException $e) {

    // Expected exception; just continue testing.
  }
  restore_error_handler();
}