Назад | Перейти на главную страницу

Перечисление динамических столбцов для предотвращения внедрения SQL

У меня есть приложение, которое полностью предназначено для внутреннего использования, но я пытаюсь убедиться, что мои базы покрыты SQL-инъекциями. Если у кого-то еще нет какой-либо информации, которая могла бы указать мне, в противном случае невозможно динамически указать список столбцов в операторе без открытия запроса до SQL-инъекции, поскольку подготовленный оператор bindparam не может использоваться для указания имени столбца.

Например (невозможно, вызовет ошибки):

SELECT :column_name FROM tablename WHERE other_column_name = :value

Чтобы по-прежнему динамически разрешать вызов имени столбца, мне нужно напрямую передать переменную (работает, но уязвимо для инъекции):

SELECT $column_name FROM tablename WHERE other_column_name = :value

Чтобы предотвратить внедрение, я проверяю значение $ column_name по белому списку всех имен столбцов в таблице, используя функцию PHP in_array. Если он есть в списке, я выполняю запрос, но если его нет, я генерирую исключение.

Поскольку в приложении есть много разных мест, где требуется подобный запрос, я добавил функцию в свою оболочку PDO для автоматического перечисления этих списков столбцов с использованием данных из базы данных information_schema:

public function schema_list($database_name = NULL, $table_name = NULL, $column_name = NULL)
{   
    $query = 'SELECT table_schema AS database_name, table_name, column_name 
        FROM `information_schema`.`columns` 
        WHERE table_schema = :database_name ';
    $parameters = array();

    // Use this object's database name unless manually specified
    if (!is_null($database_name)) {
        $parameters[':database_name'] = $database_name;
    } else {
        $parameters[':database_name'] = $this->database_detail['dbname'];
    }

    // Add details for table_name if specified
    if (!is_null($table_name)) {
        $query .= 'AND table_name = :table_name ';
        $parameters[':table_name'] = $table_name;
    }

    // Add details for column_name if specified
    if (!is_null($column_name)) {
        $query .= 'AND column_name = :column_name ';
        $parameters[':column_name'] = $column_name;
    }

    $result = $this->query_select($query, $parameters);

    if ($result['sth_count'] == 0) {
        return FALSE;
    } else {
        return $result;
    }
}

Конечно, это возвращает многомерный массив записей, конструктор объекта, которому в конечном итоге необходимо перечислить список столбцов, делает:

protected $table_process_columnlist;    // List of valid columns for the process table

public function __construct(dblink $global_dblink = NULL, job $global_job = NULL)
{

// other constructor secret sauce //

// Initialize valid column list
$this->table_process_columnlist = array();
$schema_list = $this->dblink->schema_list(NULL, $this->table_process);
foreach ($schema_list['sth_result'] as $table_column) {
    $this->table_process_columnlist[] = $table_column['column_name'];
}

И затем используется:

public function __get($column)
{
    if (in_array($column, $this->table_process_columnlist)) {
        $query = 'SELECT '.$column.'  
            FROM '.$this->table_process.' 
            WHERE idprocess = :idprocess
            LIMIT 1';
        $parameters = array (
            ':idprocess' => $this->idprocess,
        );

        $result = $this->dblink->query_select($query, $parameters);

        return $result['sth_result'][0][$column];
    } else {
        $e_message = 'Could not get specified column';
        throw new ACException($e_message, 99);
    }
}

Имейте в виду, что на самом деле я не использую подготовленные операторы для повышения производительности, они используются только для защиты от SQL-инъекций. Из того, что я обнаружил в тестах, таблица information_schema будет возвращать только набор результатов для элементов, к которым пользователь дескриптора базы данных действительно имеет доступ, поэтому функцию schema_list нельзя использовать для перечисления макета схемы базы данных, которую пользователь базы данных не делает. у t уже есть доступ к.

Я использую публичную магическую функцию __get, потому что этот класс используется только внутри приложения и заключен в оболочку другими классами, находящимися дальше в приложении.

Является ли это разумным способом предотвратить SQL-инъекцию, получив при этом возможность динамически указывать имена столбцов? Есть ли проблемы с безопасностью при использовании этого метода?

Вы можете вручную создать массив белого списка, указав в запросе его собственные уязвимости для получения белого списка.

Я не читал ваш код, но да, белый список - правильное решение.