Na verdade, existem duas perguntas em uma. E a questão do título tem muito pouco a ver com as preocupações expressas pelo OP nos comentários posteriores.
Embora eu perceba que para o OP é o caso particular que importa, para os leitores vindos do Google, é importante responder à pergunta mais geral, que pode ser formulada como "a concatenação é tão segura quanto as declarações preparadas se eu tivesse certeza que cada literal que estou concatenando é seguro? ". Então, eu gostaria de me concentrar neste último. E a resposta é
Definitivamente não.
A explicação não é tão direta como a maioria dos leitores gostaria, mas vou tentar o meu melhor.
Estive refletindo sobre o assunto por um tempo, resultando no artigo (embora baseado no ambiente PHP) onde tentei resumir tudo. Ocorreu-me que a questão da proteção contra injeção de SQL costuma ser evitada em relação a alguns tópicos relacionados, mas mais restritos, como escape de string, conversão de tipo e outros. Embora algumas das medidas possam ser consideradas seguras quando tomadas por si mesmas, não existe um sistema, nem uma regra simples a seguir. O que torna o terreno muito escorregadio, colocando muito na atenção e experiência do desenvolvedor.
A questão da injeção de SQL não pode ser simplificada para uma questão de sintaxe específica. É mais amplo do que o desenvolvedor médio costumava pensar. É uma questão metodológica também. Não se trata apenas de "Qual formatação específica devemos aplicar", mas também de " Como isso deve ser feito".
(Deste ponto de vista, um artigo de Jon Skeet citado na outra resposta está indo bem mais mal do que bem, já que está novamente criticando algum caso extremo, concentrando-se em um problema de sintaxe específico e falhando em resolver o problema como um todo.)
Quando você está tentando abordar a questão da proteção não como um todo, mas como um conjunto de diferentes questões de sintaxe, você está enfrentando uma infinidade de problemas.
- a lista de opções de formatação possíveis é realmente enorme. Significa que alguém pode facilmente ignorar alguns. Ou confunda-os (usando escape de string para identificador, por exemplo).
- A concatenação significa que todas as medidas de proteção devem ser executadas pelo programador, não pelo programa. Este problema sozinho leva a várias consequências:
- essa formatação é manual. Manual significa extremamente sujeito a erros. Pode-se simplesmente esquecer de se inscrever.
- além disso, existe a tentação de mover os procedimentos de formatação para alguma função centralizada, bagunçando ainda mais as coisas e danificando os dados que não vão para o banco de dados.
- quando mais de um desenvolvedor está envolvido, os problemas se multiplicam por um fator de dez.
- quando a concatenação é usada, não é possível identificar uma consulta potencialmente perigosa: todas são potencialmente perigosas!
Ao contrário dessa bagunça, as declarações preparadas são de fato O Santo Graal:
- pode ser expresso na forma de uma regra simples que é fácil de seguir.
- é uma medida essencialmente indissociável, significa que o desenvolvedor não pode interferir e, voluntária ou involuntariamente, estragar o processo.
- a proteção contra injeção é, na verdade, apenas um efeito colateral das instruções preparadas, cujo propósito real é produzir instruções sintaticamente corretas. E uma declaração sintaticamente correta é 100% à prova de injeção. Ainda assim, precisamos que nossa sintaxe esteja correta, apesar de qualquer possibilidade de injeção.
- se usado em todas as direções, ele protege o aplicativo independentemente da experiência do desenvolvedor. Digamos, existe uma coisa chamada injeção de segunda ordem . E uma ilusão muito forte que diz "para proteger, escapar de todas as entradas fornecidas pelo usuário ". Combinados, eles levam à injeção, se um desenvolvedor toma a liberdade de decidir o que precisa ser protegido e o que não.
(Pensando mais, descobri que o conjunto atual de marcadores de posição não é suficiente para as necessidades da vida real e deve ser estendido, tanto para as estruturas de dados complexas, como matrizes, e até mesmo palavras-chave SQL ou identificadores, que às vezes precisam ser adicionados ao consulta dinamicamente também, mas um desenvolvedor é deixado desarmado para tal caso e forçado a voltar para a concatenação de string, mas isso é uma outra questão).
Curiosamente, a controvérsia desta questão é provocada pela natureza muito controversa de Stack Overflow. A ideia do site é fazer uso de perguntas específicas de usuários que perguntam diretamente para atingir o objetivo de ter um banco de dados de respostas de propósito geral adequado para usuários que vêm de pesquisa . A ideia não é ruim em si , mas falha em uma situação como esta: quando um usuário faz uma pergunta muito restrita , principalmente para obter uma discussão em uma disputa com um colega (ou para decidir se vale a pena refatorar o código). Enquanto a maioria dos participantes experientes tenta escrever uma resposta, tendo em mente a missão de Stack Overflow como um todo, tornando sua resposta adequada para o maior número possível de leitores, não apenas para o OP.