As declarações podem PHP DOP aceitar o nome de tabela ou coluna como parâmetro?

StackOverflow https://stackoverflow.com/questions/182287

  •  06-07-2019
  •  | 
  •  

Pergunta

Por que não posso passar o nome da tabela com um comunicado DOP preparado?

$stmt = $dbh->prepare('SELECT * FROM :table WHERE 1');
if ($stmt->execute(array(':table' => 'users'))) {
    var_dump($stmt->fetchAll());
}

Existe outra maneira segura para inserir um nome de tabela em uma consulta SQL? Com seguro, quero dizer que eu não quero fazer

$sql = "SELECT * FROM $table WHERE 1"
Foi útil?

Solução

tabela e coluna nomes não podem ser substituídos por parâmetros em PDO.

Nesse caso, você simplesmente quer filtro e higienizar os dados manualmente. Uma maneira de fazer isso é passar parâmetros de taquigrafia para a função que irá executar a consulta de forma dinâmica e, em seguida, usar uma instrução switch() para criar uma lista branca de valores válidos para ser usado para o nome da tabela ou coluna. Dessa forma, nenhuma entrada de usuário nunca vai diretamente para a consulta. Assim, por exemplo:

function buildQuery( $get_var ) 
{
    switch($get_var)
    {
        case 1:
            $tbl = 'users';
            break;
    }

    $sql = "SELECT * FROM $tbl";
}

Ao deixar nenhum caso padrão ou usando um caso padrão que retorna uma mensagem de erro que você garantir que os únicos valores que você quer se acostumar usado.

Outras dicas

Para entender por obrigatório uma tabela (ou coluna) nome não funcionar, você tem que entender como os espaços reservados em declarações trabalho preparado: eles não são simplesmente substituídos como (adequadamente escape) cordas eo SQL resultante executado. Em vez disso, um SGBD pediu para "preparar" uma declaração surge com um plano de consulta completa de como ele iria executar essa consulta, incluindo as tabelas e índices que usaria, o que será o mesmo, independentemente de como você preencher os espaços reservados.

O plano para SELECT name FROM my_table WHERE id = :value será o mesmo que você substituir :value, mas o SELECT name FROM :table WHERE id = :value aparentemente semelhante não pode ser planejado, porque o DBMS tem idéia do que a tabela que você está realmente indo para escolher.

Isto não é algo que uma biblioteca de abstração como DOP pode ou deve contornar, tampouco, uma vez que seria derrotar os 2 fins-chave de instruções preparadas: 1) para permitir que o banco de dados para decidir de antemão como uma consulta será executado, e usar o mesmo plano várias vezes; e 2) para evitar problemas de segurança, separando a lógica da consulta a partir da entrada variável.

Eu vejo este é um post antigo, mas eu achei que seria útil e pensei que iria partilhar uma solução semelhante ao que @kzqai sugerido:

Eu tenho uma função que recebe dois parâmetros como ...

function getTableInfo($inTableName, $inColumnName) {
    ....
}

Dentro I cheque contra matrizes Eu configurei para certificar-se apenas as tabelas e colunas com "abençoado" tabelas são acessíveis:

$allowed_tables_array = array('tblTheTable');
$allowed_columns_array['tblTheTable'] = array('the_col_to_check');

Em seguida, a verificação PHP antes de executar olhares DOP como ...

if(in_array($inTableName, $allowed_tables_array) && in_array($inColumnName,$allowed_columns_array[$inTableName]))
{
    $sql = "SELECT $inColumnName AS columnInfo
            FROM $inTableName";
    $stmt = $pdo->prepare($sql); 
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}

Usando o primeiro não é inerentemente mais seguro do que o último, você precisa higienizar a entrada se é parte de uma matriz de parâmetro ou uma variável simples. Então, eu não vejo nada de errado em usar o último formulário com $table, desde que você se certificar de que o conteúdo de $table é seguro (alphanum mais sublinhados?) Antes de usá-lo.

(resposta tardia, consultar o meu lado nota).

A mesma regra se aplica quando se tenta criar um "banco de dados".

Você não pode usar uma declaração preparada para vincular um banco de dados.

ou seja:.

CREATE DATABASE IF NOT EXISTS :database

não vai funcionar. Use uma lista segura em seu lugar.

Nota lateral: Eu adicionei esta resposta (como uma comunidade wiki), porque muitas vezes usado para questões estreitas com, onde algumas pessoas postadas perguntas semelhantes a este na tentativa de vincular um banco de dados e não uma mesa e / ou coluna.

Uma parte de mim quer saber se você poderia fornecer sua própria função de sanitização costume tão simples como isto:

$value = preg_replace('/[^a-zA-Z_]*/', '', $value);

Eu realmente não tenho pensado por ele, mas parece que a remoção de qualquer coisa, exceto caracteres e sublinhados podem funcionar.

Quanto à questão principal neste segmento, os outros postos esclareceu porque é que não podemos valores ligam a nomes de coluna na preparação das demonstrações, então aqui está uma solução:

class myPdo{
    private $user   = 'dbuser';
    private $pass   = 'dbpass';
    private $host   = 'dbhost';
    private $db = 'dbname';
    private $pdo;
    private $dbInfo;
    public function __construct($type){
        $this->pdo = new PDO('mysql:host='.$this->host.';dbname='.$this->db.';charset=utf8',$this->user,$this->pass);
        if(isset($type)){
            //when class is called upon, it stores column names and column types from the table of you choice in $this->dbInfo;
            $stmt = "select distinct column_name,column_type from information_schema.columns where table_name='sometable';";
            $stmt = $this->pdo->prepare($stmt);//not really necessary since this stmt doesn't contain any dynamic values;
            $stmt->execute();
            $this->dbInfo = $stmt->fetchAll(PDO::FETCH_ASSOC);
        }
    }
    public function pdo_param($col){
        $param_type = PDO::PARAM_STR;
        foreach($this->dbInfo as $k => $arr){
            if($arr['column_name'] == $col){
                if(strstr($arr['column_type'],'int')){
                    $param_type = PDO::PARAM_INT;
                    break;
                }
            }
        }//for testing purposes i only used INT and VARCHAR column types. Adjust to your needs...
        return $param_type;
    }
    public function columnIsAllowed($col){
        $colisAllowed = false;
        foreach($this->dbInfo as $k => $arr){
            if($arr['column_name'] === $col){
                $colisAllowed = true;
                break;
            }
        }
        return $colisAllowed;
    }
    public function q($data){
        //$data is received by post as a JSON object and looks like this
        //{"data":{"column_a":"value","column_b":"value","column_c":"value"},"get":"column_x"}
        $data = json_decode($data,TRUE);
        $continue = true;
        foreach($data['data'] as $column_name => $value){
            if(!$this->columnIsAllowed($column_name)){
                 $continue = false;
                 //means that someone possibly messed with the post and tried to get data from a column that does not exist in the current table, or the column name is a sql injection string and so on...
                 break;
             }
        }
        //since $data['get'] is also a column, check if its allowed as well
        if(isset($data['get']) && !$this->columnIsAllowed($data['get'])){
             $continue = false;
        }
        if(!$continue){
            exit('possible injection attempt');
        }
        //continue with the rest of the func, as you normally would
        $stmt = "SELECT DISTINCT ".$data['get']." from sometable WHERE ";
        foreach($data['data'] as $k => $v){
            $stmt .= $k.' LIKE :'.$k.'_val AND ';
        }
        $stmt = substr($stmt,0,-5)." order by ".$data['get'];
        //$stmt should look like this
        //SELECT DISTINCT column_x from sometable WHERE column_a LIKE :column_a_val AND column_b LIKE :column_b_val AND column_c LIKE :column_c_val order by column_x
        $stmt = $this->pdo->prepare($stmt);
        //obviously now i have to bindValue()
        foreach($data['data'] as $k => $v){
            $stmt->bindValue(':'.$k.'_val','%'.$v.'%',$this->pdo_param($k));
            //setting PDO::PARAM... type based on column_type from $this->dbInfo
        }
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);//or whatever
    }
}
$pdo = new myPdo('anything');//anything so that isset() evaluates to TRUE.
var_dump($pdo->q($some_json_object_as_described_above));

A descrição acima é apenas um exemplo, escusado será dizer que, contra cópia> colar não vai funcionar. Ajuste para suas necessidades. Agora isso pode não fornecer segurança 100%, mas permite algum controle sobre os nomes das colunas quando "entrar" como cordas dinâmicas e podem ser alteradas em usuários finais. Além disso, não há necessidade de construir alguma matriz com os nomes das colunas da tabela e tipos, uma vez que são extraídos do information_schema.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top