Posso usar uma instrução preparada por PDO para vincular um identificador (uma tabela ou nome de campo) ou uma palavra-chave de sintaxe?
Infelizmente, a instrução preparada pode representar apenas um literal de dados. Então, uma armadilha muito comum é uma consulta como esta:
$opt = "id";
$sql = "SELECT :option FROM t WHERE id=?";
$stm = $pdo->prepare($sql);
$stm->execute(array($opt));
$data = $stm->fetchAll();
Depende das configurações do PDO, esta consulta resultará em erro (no caso de usar instruções preparadas reais) ou apenas uma string literal
'id'
no fieldset (no caso de preparações emuladas). Portanto, um desenvolvedor precisa cuidar dos identificadores por conta própria - o PDO não oferece nenhuma ajuda para este assunto.
Para tornar um identificador dinâmico seguro, é preciso seguir 2 regras estritas:
- formatar o identificador corretamente
- para verificá-lo em uma lista de permissões codificada .
Para formatar um identificador, deve-se aplicar estas 2 regras:
- Inclua o identificador entre acentos graves.
- Escape dos backticks dobrando-os.
Após essa formatação, é seguro inserir a variável $table na consulta. Assim, o código seria:
$field = "`".str_replace("`","``",$field)."`";
$sql = "SELECT * FROM t ORDER BY $field";
No entanto, embora tal formatação seja suficiente para os casos como ORDER BY, para a maioria dos outros casos existe a possibilidade de um tipo diferente de injeção:deixando um usuário escolher uma tabela ou um campo que pode ver, podemos revelar algumas informações confidenciais, como senha ou outros dados pessoais. Portanto, é sempre melhor verificar os identificadores dinâmicos em uma lista de valores permitidos. Aqui está um breve exemplo:
$allowed = array("name","price","qty");
$key = array_search($_GET['field'], $allowed);
$field = $allowed[$key];
$query = "SELECT $field FROM t"; //value is safe
Para palavras-chave, as regras são as mesmas, mas é claro que não há formatação disponível - portanto, apenas a lista de permissões é possível e deve ser usada:
$dir = $_GET['dir'] == 'DESC' ? 'DESC' : 'ASC';
$sql = "SELECT * FROM t ORDER BY field $dir"; //value is safe
Veja também esta nota de contribuição do usuário na documentação do PHP:Nota do usuário sobre PDO::quote