Blog de SQLFix
Consultas parametrizadas vs SQL generado
Un modelo que escribe una consulta con buena apariencia no ha resuelto la inyección. La propiedad de seguridad que importa radica en cómo la consulta se encuentra con los datos, y ese es el trabajo de las consultas parametrizadas con declaraciones preparadas. El texto generado es un punto de partida para la revisión; no es un sustituto del límite que las declaraciones preparadas imponen entre el código y los datos.
Qué hace realmente una sentencia preparada
Cuando el controlador recibe una solicitud de preparación, envía el texto SQL al servidor. El servidor analiza el texto, crea un plan de ejecución y almacena ese plan. Más tarde, cuando la aplicación llama a execute, el controlador envía solo los valores de los parámetros. El servidor aplica el plan almacenado a esos valores sin volver a analizar el texto SQL. Debido a que los valores nunca se convierten en parte de la cadena de la sentencia, una carga como ' OR 1=1-- se trata como un valor literal, no como parte del comando.
He visto que esto funciona en MySQL 5.0, PostgreSQL 8.0, SQL Server 2005 y Oracle 9i. Cada lenguaje principal proporciona una API de enlace: el PreparedStatement de JDBC, el cursor.execute de Python con marcadores de posición, el database/sql.Query de Go y el pool.execute de Node. El modelo puede generar la forma SELECT * FROM orders WHERE customer_id =? AND status =? ; el código circundante suministra los dos valores en tiempo de ejecución.
Si ve la comilla dentro del texto de la sentencia, no está utilizando una sentencia preparada real; es probable que esté realizando una interpolación de cadenas en el lado del cliente, lo que no proporciona la seguridad contra inyecciones.
Por qué el SQL revisado todavía necesita parámetros
La revisión humana puede confirmar que la forma de la consulta coincide con la regla de negocio prevista. Puede detectar joins faltantes, nombres de columnas incorrectos o filtros ineficientes. Sin embargo, los valores que fluyen hacia esos marcadores de posición todavía provienen de fuentes externas: formularios web, cargas de API, colas de mensajes o archivos heredados. Esas fuentes no están bajo el control directo del desarrollador en el momento de la ejecución.
La hoja de trucos de prevención de inyección de SQL de OWASP establece claramente: "Opción de defensa 1: Declaraciones preparadas (Consultas parametrizadas)". La hoja de trucos no enumera la "aprobación humana" como sustituto. La aprobación aborda la corrección lógica; la parametrización aborda el límite de confianza entre el código y los datos. Dejar caer cualquiera de los dos deja al otro sin protección.
Aunque el revisor humano vio una declaración INSERT sensata, la inyección ocurrió en el paso de enlace de valores, que la revisión no cubrió.
De esta manera, el trabajo del revisor sigue siendo valioso, y el paso de ejecución mantiene su garantía de seguridad.
Los procedimientos almacenados no son seguridad automática
Muchos equipos asumen que mover la lógica a un procedimiento almacenado elimina el riesgo de inyección. Esa suposición falla cuando el propio procedimiento construye SQL dinámico.
Considere un procedimiento escrito en T‑SQL:
CREATE PROCEDURE GetOrders @Customer NVARCHAR(50) AS BEGIN DECLARE @sql NVARCHAR(MAX) = N'SELECT * FROM Orders WHERE Customer = ''' + @Customer + ''''; EXEC sp_executesql @sql; END Si @Customer contiene '); DROP TABLE Orders;-- , la cadena resultante se convierte en un lote válido que elimina la tabla. El wrapper del procedimiento no protegió los datos; la concatenación dentro del procedimiento lo hizo.
El mismo patrón aparece en PL/SQL con EXECUTE IMMEDIATE, en PostgreSQL con EXECUTE format, y en MySQL con PREPARE/EXECUTE construido a partir de una variable.
Después de la reescritura, ejecute el procedimiento con un valor que contenga una comilla y verifique que el error es una incoherencia de tipo de datos, no un error de sintaxis.
Mantener la generación y la ejecución como capas separadas
La arquitectura más segura trata al modelo de inteligencia artificial como un motor de sugerencias, no como un componente de acceso a datos. El modelo produce una forma de consulta candidata. Un revisor de política automatizado o humano revisa esa forma para su corrección, cumplimiento y rendimiento. Solo después de la aprobación, el código de la aplicación toma la forma, la prepara con el controlador y enlaza los parámetros en tiempo real.
Dado que el modelo nunca ve una conexión en vivo, no puede ser engañado para revelar detalles del esquema a través de mensajes de error. Dado que la capa de ejecución nunca confía en cadenas crudas, incluso un modelo comprometido no puede causar inyección.
Este proceso mantiene la salida del modelo en el bucle de revisión y garantiza que el paso de ejecución utilice solo declaraciones parametrizadas.
Qué no es esto
Quiero dejar claro los límites del enfoque descrito anteriormente. No garantiza que se detecten todos los posibles errores. No promete una protección del 100 % contra todas las clases de fallos. No afirma eliminar la necesidad de otras defensas como la validación de entrada, la codificación de salida o las cuentas de base de datos con privilegios mínimos.
Lo que hace el método es:
Si solo se confía en la salida del modelo y se salta la revisión o el paso de enlace de parámetros, se pierde la protección. Si se permite que la capa de ejecución concatene valores después de la preparación, se vuelve a introducir el riesgo. La técnica es un componente de una estrategia de defensa en profundidad, no una bala de plata autónoma.
Ejemplo de flujo de trabajo en el mundo real
Imagina un producto SaaS que permite a los administradores exportar una lista de facturas vencidas. El producto utiliza SQLForm para redactar la consulta.
Esta traza muestra cómo el paso de generación permanece independiente del paso de ejecución, y cómo el límite de parámetro bloquea la inyección incluso cuando el usuario intenta introducir SQL en un campo que el modelo nunca ve.
Errores comunes y cómo los evitamos
Incluso con un diseño sólido, los equipos a veces caen en hábitos que debilitan la protección. A continuación, se presentan errores frecuentes que he observado y los pasos concretos que seguimos para mantener el sistema seguro.
Algunos desarrolladores crean SELECT * FROM tags WHERE tag IN (?,?,?) creando primero una cadena con el número correcto de marcadores de posición, y luego concatenando los valores directamente en el SQL antes de prepararlo. Esto anula el propósito porque los valores se convierten en parte del texto de la instrucción.
Una instrucción preparada para SELECT * FROM orders WHERE status =? se ejecuta más tarde con un valor que en realidad contiene una nueva instrucción SELECT completa, confiando en que el controlador trate el valor como datos. Aunque el valor está citado de manera segura, la lógica de la aplicación puede ser incorrecta porque el desarrollador asumió que la instrucción podría manejar cualquier SQL.
Los paneles de control internos a veces omiten la puerta de revisión humana porque el equipo asume que los datos son de confianza. Más tarde, una cuenta de servicio es comprometida y comienza a alimentar cargas maliciosas en el panel de control.
Para depurar, los equipos a veces registran los valores exactos de los parámetros. Si esos registros se exponen, pueden filtrar datos sensibles como contraseñas o información de identificación personal.
Un ORM puede ofrecer un método que parece aceptar parámetros, pero en el fondo construye una cadena y llama a ExecuteNonQuery en una cadena SQL sin procesar.
Al vigilar estos errores y aplicar las soluciones correspondientes, la separación entre generación y ejecución sigue siendo sólida, y el límite parametrizado sigue haciendo el trabajo para el que fue diseñado.
Referencias
https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
https://cwe.mitre.org/data/definitions/798.html
https://cwe.mitre.org/data/definitions/89
Preguntas frecuentes
No. La inyección depende de cómo los valores cumplen con la consulta, no de si la consulta es lógicamente correcta. Las consultas parametrizadas mantienen los valores separados del texto de comando independientemente del contenido de los valores.
No de forma automática. Un procedimiento que concatena la entrada y la ejecuta con EXECUTE IMMEDIATE aún puede inyectar. La seguridad proviene del enlace de parámetros dentro del procedimiento.
Un ORM es seguro solo cuando se utiliza su interfaz parametrizada. Construir una cadena y pasarla al ORM reintroduce la inyección. El límite entre código y datos todavía debe ser aplicado.
No. Mantenga la generación y la ejecución separadas: el modelo crea borradores, una puerta aprueba y el código de la aplicación enlaza parámetros en tiempo de ejecución. El modelo nunca debe ser la ruta de ejecución.
Pruebe el producto: SQLForm
2026-09-16 · solo fuentes primarias · no hay métricas de tráfico fabricadas
Artículos relacionados
Lenguaje natural a SQL: comprobaciones de seguridad que importan
Explora la matriz completa de temas de AI micro-SaaS →
¿Protege una consulta correcta contra la inyección SQL?
No. La inyección depende de cómo los valores se relacionan con la consulta, no de si la consulta es lógicamente correcta. Las consultas parametrizadas mantienen los valores separados del texto de comando independientemente del contenido de los valores.
¿Son los procedimientos almacenados seguros por defecto?
No automáticamente. Un procedimiento que concatena la entrada y la ejecuta con EXECUTE IMMEDIATE aún puede inyectar. La seguridad proviene del enlace de parámetros dentro del procedimiento.
¿Puede un ORM reemplazar las consultas parametrizadas?
Un ORM es seguro solo cuando se utiliza su interfaz parametrizada. Construir una cadena y pasarla al ORM vuelve a introducir la inyección. El límite entre el código y los datos todavía tiene que ser aplicado.
¿Debería un modelo contener alguna vez la conexión de base de datos?
No. Mantenga la generación y la ejecución separadas: el modelo crea un borrador, una puerta aprueba y el código de la aplicación enlaza parámetros en tiempo de ejecución. El modelo nunca debería ser la ruta de ejecución.
Solo para apoyo a la toma de decisiones. No sustituye la revisión del administrador de base de datos ni las pruebas de carga de producción. No se garantiza el rendimiento.