Eu tenho um procedimento armazenado simples:
DELIMITER $$
CREATE PROCEDURE `findUserByName`(IN `name` VARCHAR(36))
BEGIN
set @name = name;
PREPARE findStmt FROM 'SELECT * FROM user WHERE name=?';
EXECUTE findStmt USING @name;
END$$
Eu chamo isso em PHP com o método prepare:
$stmt = $mysqli->prepare('CALL findUserByName(?)');
$stmt->execute([$inputName]);
Tenho 2 perguntas sobre o código acima:
Esta instrução de atribuição
set @name = name;
parece um tanto redundante. Posso usar o parâmetro diretamentename
? O MySQL não me permite usarUSING name
, também tentei alterar o nome do parâmetro para@name
, mas não será reconhecido.Como usei o método prepare em PHP, é seguro concatenar o parâmetro com SQL?
set @sql = CONCAT('SELECT * FROM user WHERE name=''', name, ''''); PREPARE findStmt FROM @sql; EXECUTE findStmt;
Selecione um nome distinto para o parâmetro do procedimento, evitando duplicação com o nome da coluna. Você pode então usar o parâmetro diretamente na consulta:
EXECUTE
. Concordo que isso é estranho e insatisfatório, porque você deve definir uma variável definida pelo usuário (o tipo com o@
sigilo) que parece redundante.https://dev.mysql.com/doc/refman/8.0/en/execute.html diz:
Não, não é seguro concatenar strings em seu SQL dinâmico, se a string puder conter conteúdo não confiável. Isso é chamado de injeção de SQL e é uma fonte comum de erros ou violações de dados.
O que acontece no exemplo acima, se alguém inserir o nome como "O'Reilly"?
Você pode ver conselhos para "usar instruções preparadas" para proteger seu código contra injeção de SQL, mas apenas usar
PREPARE
não é mágica. Não é uma bênção SQL inseguro torná-lo seguro. O conselho deveria ser “usar parâmetros”. É necessário usarPREPARE
parâmetros, mas são os parâmetros, e não oPREPARE
, que é a parte importante para torná-lo seguro.Re seu comentário:
Se sua consulta for corrigida no momento em que você cria o procedimento, e as variáveis que você combina com ela estão apenas substituindo valores escalares, então você não precisa usar
PREPARE/EXECUTE
.Se a sintaxe da sua consulta for parcialmente baseada na lógica do procedimento, você deverá usar SQL dinâmico. Ou seja, você não só precisa combinar valores escalares com a consulta na forma de variáveis, mas também modificar a própria consulta, por exemplo, para modificar expressões ou palavras-chave — qualquer coisa além de valores escalares simples.
Você também pode estar interessado na minha resposta aqui: Injeção de SQL em procedimentos armazenados via MySQL Connector. Escrevi isso para explicar melhor a natureza de
PREPARE/EXECUTE
quando a consulta é analisada. É importante saber isso para entender como a injeção de SQL é um risco.