En realidad, hay dos preguntas en una. Y la pregunta del título tiene muy poco que ver con las preocupaciones expresadas por el OP en los comentarios posteriores.
Aunque me doy cuenta de que para el OP es su caso particular lo que importa, para los lectores que vienen de Google, es importante responder a la pregunta más general, que puede expresarse como "¿Es la concatenación tan segura como las declaraciones preparadas si me asegure que cada literal que estoy concatenando es seguro? ". Entonces, me gustaría concentrarme en este último. Y la respuesta es
Definitivamente no.
La explicación no es tan directa como le gustaría a la mayoría de los lectores, pero haré todo lo posible.
He estado reflexionando sobre el tema durante un tiempo, lo que resultó en el artículo (aunque basado en el entorno PHP) donde traté de resumir todo. Se me ocurrió que la cuestión de la protección contra la inyección de SQL a menudo se elude hacia algunos temas relacionados pero más estrechos, como el escape de cadenas, la conversión de tipos y demás. Aunque algunas de las medidas pueden considerarse seguras cuando se toman por sí mismas, no existe un sistema ni una regla simple a seguir. Lo que hace que sea un terreno muy resbaladizo, poniendo demasiado en la atención y experiencia del desarrollador.
La cuestión de la inyección de SQL no se puede simplificar a una cuestión de sintaxis en particular. Es más amplio de lo que pensaba el desarrollador promedio. También es una cuestión metodológica . No se trata solo de "Qué formato en particular debemos aplicar", sino también de " Cómo se debe hacer".
(Desde este punto de vista, un artículo de Jon Skeet citado en la otra respuesta lo está haciendo bastante mal que bien, ya que nuevamente es quisquilloso en algún caso límite, se concentra en un problema de sintaxis en particular y no aborda el problema en su totalidad).
Cuando intenta abordar la cuestión de la protección no como un todo, sino como un conjunto de diferentes problemas de sintaxis, se enfrenta a una multitud de problemas.
- la lista de posibles opciones de formato es realmente enorme. Significa que uno puede fácilmente pasar por alto algunos. O confúndalos (por ejemplo, utilizando cadena de escape para el identificador ).
- La concatenación significa que todas las medidas de protección deben ser realizadas por el programador, no por el programa. Este problema por sí solo tiene varias consecuencias:
- tal formateo es manual. Manual significa extremadamente propenso a errores. Uno podría simplemente olvidarse de aplicar.
- además, existe la tentación de trasladar los procedimientos de formateo a alguna función centralizada, estropeando aún más las cosas y estropeando los datos que no van a la base de datos.
- cuando hay más de un desarrollador involucrado, los problemas se multiplican por diez.
- cuando se utiliza la concatenación, no se puede decir una consulta potencialmente peligrosa de un vistazo: ¡ todas son potencialmente peligrosas!
A diferencia de ese lío, las declaraciones preparadas son de hecho El Santo Grial:
- se puede expresar en forma de una regla simple que es fácil de seguir.
- es una medida esencialmente irrompible, significa que el desarrollador no puede interferir y, voluntaria o involuntariamente, estropear el proceso.
- la protección contra la inyección es en realidad sólo un efecto secundario de las declaraciones preparadas, cuyo propósito real es producir declaraciones sintácticamente correctas. Y una declaración sintácticamente correcta es 100% a prueba de inyecciones. Sin embargo, necesitamos que nuestra sintaxis sea correcta a pesar de cualquier posibilidad de inyección.
- si se usa en todos los sentidos, protege la aplicación independientemente de la experiencia del desarrollador. Diga, hay algo que se llama inyección de segundo orden . Y un engaño muy fuerte que dice "para proteger, escapar de todas las entradas proporcionadas por el usuario ". Combinados, conducen a la inyección, si un desarrollador se toma la libertad de decidir qué debe protegerse y qué no.
(Pensando más, descubrí que el conjunto actual de marcadores de posición no es suficiente para las necesidades de la vida real y debe extenderse, tanto para estructuras de datos complejas, como matrices, e incluso palabras clave SQL o identificadores, que a veces deben agregarse al consulta dinámicamente también, pero un desarrollador se queda desarmado para tal caso y se ve obligado a recurrir a la concatenación de cadenas, pero eso es otra cuestión).
Curiosamente, la controversia de esta pregunta es provocada por la naturaleza muy controvertida de Stack Overflow. La idea del sitio es hacer uso de preguntas particulares de los usuarios que preguntan directamente para lograr el objetivo de tener una base de datos de respuestas de propósito general adecuada para los usuarios que provienen de la búsqueda . La idea no es mala per se , pero falla en una situación como esta: cuando un usuario hace una pregunta muy limitada , particularmente para tener una discusión en una disputa con un colega (o para decidir si vale la pena refactorizar el código). Si bien la mayoría de los participantes experimentados están tratando de escribir una respuesta, teniendo en cuenta la misión de Stack Overflow en su totalidad, lo que hace que su respuesta sea buena para tantos lectores como sea posible, no solo para el OP.