¿Garantía de 100% seguro de inyección SQL? No lo conseguiré (de mí).
En principio, su base de datos (o biblioteca en su idioma que interactúa con la base de datos) podría implementar sentencias preparadas con parámetros enlazados de una manera insegura susceptible de algún tipo de ataque avanzado, por ejemplo, explotar los desbordamientos de búfer o tener caracteres que terminen en cero. en cadenas proporcionadas por el usuario, etc. (Podría argumentar que estos tipos de ataques no deberían llamarse inyección SQL, ya que son fundamentalmente diferentes; pero eso es solo semántica).
Nunca he oído hablar de ninguno de estos ataques en declaraciones preparadas en bases de datos reales en el campo y sugiero encarecidamente el uso de parámetros enlazados para evitar la inyección de SQL. Sin parámetros enlazados ni saneamiento de entrada, es trivial hacer inyección de SQL. Con solo el saneamiento de entrada, a menudo es posible encontrar una laguna oscura alrededor del saneamiento.
Con los parámetros enlazados, el plan de ejecución de su consulta SQL se resuelve con anticipación sin depender de la entrada del usuario, lo que debería hacer que la inyección de SQL no sea posible (ya que las comillas, los símbolos de comentarios, etc. se insertan solo dentro de la declaración de SQL ya compilada ).
El único argumento en contra del uso de declaraciones preparadas es que desea que su base de datos optimice sus planes de ejecución en función de la consulta real. La mayoría de las bases de datos, cuando se realiza la consulta completa, son lo suficientemente inteligentes como para hacer un plan de ejecución óptimo; por ejemplo, si la consulta devuelve un gran porcentaje de la tabla, querría recorrer toda la tabla para encontrar coincidencias; mientras que si solo va a obtener algunos registros, puede hacer una búsqueda basada en índices [1] .
EDITAR: Respondiendo a dos críticas (que son un poco demasiado largas para comentarios):
Primero, como otros anotaron sí, todas las bases de datos relacionales que admiten declaraciones preparadas y parámetros enlazados no necesariamente compilan previamente la declaración preparada sin mirar el valor de los parámetros enlazados. Muchas bases de datos suelen hacer esto, pero también es posible que las bases de datos analicen los valores de los parámetros enlazados al determinar el plan de ejecución. Esto no es un problema, ya que la estructura de la declaración preparada con parámetros enlazados separados, facilita que la base de datos pueda diferenciar claramente la instrucción SQL (incluidas las palabras clave SQL) de los datos en los parámetros enlazados (donde no habrá nada en un parámetro enlazado). interpretado como una palabra clave SQL). Esto no es posible cuando se construyen sentencias de SQL a partir de concatenación de cadenas donde las variables y las palabras clave de SQL se entremezclan.
En segundo lugar, como lo señala la otra respuesta , utiliza parámetros enlazados al llamar a una declaración SQL en un momento dado un programa evitará con seguridad la inyección de SQL al realizar esa llamada de nivel superior. Sin embargo, si tiene vulnerabilidades de inyección de SQL en otra parte de la aplicación (por ejemplo, en las funciones definidas por el usuario que almacenó y ejecutó en su base de datos que escribió de manera insegura para construir consultas SQL mediante concatenación de cadenas).
Por ejemplo, si en su aplicación escribió pseudocódigo como:
sql_stmt = "SELECT create_new_user(?, ?)"
params = (email_str, hashed_pw_str)
db_conn.execute_with_params(sql_stmt, params)
No puede haber inyección SQL cuando se ejecuta esta declaración SQL en el nivel de la aplicación. Sin embargo, si la función de la base de datos definida por el usuario fue escrita de forma poco segura (utilizando la sintaxis PL / pgSQL):
CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
sql_str TEXT;
BEGIN
sql_str := 'INSERT INTO users VALUES (' || email_str || ', ' || hashed_pw_str || ');'
EXECUTE sql_str;
END;
$$
LANGUAGE plpgsql;
entonces sería vulnerable a los ataques de inyección de SQL, ya que ejecuta una instrucción SQL construida a través de la concatenación de cadenas que mezcla la declaración SQL con cadenas que contienen los valores de las variables definidas por el usuario.
Dicho esto, a menos que intentara ser inseguro (construir sentencias de SQL mediante concatenación de cadenas), sería más natural escribir el definido por el usuario de una manera segura como:
CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
BEGIN
INSERT INTO users VALUES (email_str, hashed_pw_str);
END;
$$
LANGUAGE plpgsql;
Además, si realmente sintió la necesidad de componer una declaración SQL a partir de una cadena en una función definida por el usuario, aún puede separar las variables de datos de la declaración SQL de la misma manera que en los parámetros_contenidos / contenidos almacenados incluso dentro de una función definida por el usuario . Por ejemplo, en PL / pgSQL :
CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
sql_str TEXT;
BEGIN
sql_str := 'INSERT INTO users VALUES($1, $2)'
EXECUTE sql_str USING email_str, hashed_pw_str;
END;
$$
LANGUAGE plpgsql;
Por lo tanto, el uso de sentencias preparadas está a salvo de la inyección de SQL, siempre y cuando no solo hagas cosas inseguras en otro lugar (es decir, la construcción de sentencias de SQL por concatenación de cadenas).