Eu não li o seu código, mas sim, uma lista branca é a solução correta aqui.
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?
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.