Enumeração de coluna dinâmica para impedir a injeção de SQL

1

Eu tenho um aplicativo que é completamente para uso interno, mas estou tentando garantir que minhas bases sejam cobertas pela injeção de SQL. A menos que alguém tenha alguma informação para me apontar, caso contrário não há como especificar dinamicamente a listagem da coluna dentro de uma instrução sem abrir a consulta até a injeção SQL como declaração preparada. Bindparam não pode ser usado para especificar um nome de coluna.

Por exemplo (não é possível, lançaria erros):

SELECT :column_name FROM tablename WHERE other_column_name = :value

Para permitir dinamicamente a chamada do nome da coluna, tenho que passar diretamente em uma variável (funciona, mas é vulnerável à injeção):

SELECT $column_name FROM tablename WHERE other_column_name = :value

Para evitar a injeção, eu verifico o valor de $ column_name em relação a uma lista branca de todos os nomes de coluna dentro da tabela usando a função in_array do PHP. Se estiver na lista, vou em frente com a consulta, mas se não for, eu lanço uma exceção.

Como há muitos locais diferentes no aplicativo em que uma consulta como essa é necessária, adicionei uma função ao meu wrapper PDO para enumerar automaticamente essas listagens de colunas usando os dados do banco de dados 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;
    }
}

É claro que isso retorna uma matriz multidimensional de registros, o construtor do objeto que eventualmente precisa enumerar a listagem da coluna:

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'];
}

E então é usado por:

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);
    }
}

Lembre-se de que não estou realmente usando instruções preparadas para obter desempenho, elas estão sendo usadas apenas para proteção de injeção de SQL. Pelo que eu encontrei em testes, a tabela information_schema retornará apenas um conjunto de resultados para itens aos quais o usuário do manipulador de banco de dados realmente tem acesso, de modo que a função schema_list não poderia ser usada para enumerar o layout de esquema de um banco de dados que o usuário do banco de dados não Já tenho acesso a.

Estou usando uma função mágica __get pública porque essa classe é usada apenas internamente no aplicativo e é agrupada por outras classes no aplicativo.

Esta é uma maneira sadia de impedir a injeção de SQL enquanto se obtém a capacidade de especificar dinamicamente os nomes das colunas? Existe alguma preocupação de segurança com este método?

    
por crashmaxed 03.05.2013 / 23:24

2 respostas

0

Eu não li o seu código, mas sim, uma lista branca é a solução correta aqui.

    
por 03.05.2013 / 23:45
0

Você pode criar manualmente uma matriz de lista de permissões, iluminando a consulta com suas próprias vulnerabilidades para obter a lista de permissões.

    
por 27.06.2016 / 05:29

Tags