function QueryTest::testConditionOperatorArgumentsSQLInjection

Same name in other branches
  1. 9 core/tests/Drupal/KernelTests/Core/Database/QueryTest.php \Drupal\KernelTests\Core\Database\QueryTest::testConditionOperatorArgumentsSQLInjection()
  2. 8.9.x core/tests/Drupal/KernelTests/Core/Database/QueryTest.php \Drupal\KernelTests\Core\Database\QueryTest::testConditionOperatorArgumentsSQLInjection()
  3. 11.x 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() : void {
    $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();
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.