Blog

Text-to-SQL seguro: cómo dejar que la IA escriba consultas sin poner en riesgo tu base de datos

Anderson Sinaluisa4 min de lectura
Las capas de protección que considero obligatorias al generar SQL con IA: contexto de esquema sin datos, modo solo lectura, validación con un parser, LIMIT y revisión humana.

Generar SQL a partir de lenguaje natural es de esas demos que enamoran en cinco minutos: escribes “los 10 clientes que más compraron el último mes” y aparece la consulta perfecta. El problema empieza cuando conectas esa demo a una base de datos real.

Mientras construía SQL Copilot tuve que pensar en esto desde el día uno. Un modelo de lenguaje no es determinista, puede malinterpretar una instrucción y, si lo dejas, puede escribir un DELETE con la misma confianza con la que escribe un SELECT. Estas son las capas de protección que considero obligatorias.

Cinco capas entre la pregunta y la base de datos: esquema sin datos, parser solo SELECT, LIMIT y timeout, revisión humana y usuario de solo lectura

1. Dale contexto del esquema, no de los datos

Para generar buen SQL el modelo necesita saber qué tablas, columnas, tipos y relaciones existen. No necesita ver tus filas.

-- Esto sí se envía como contexto
CREATE TABLE orders (
  id          bigint PRIMARY KEY,
  customer_id bigint REFERENCES customers(id),
  total       numeric(12,2),
  created_at  timestamptz
);

El esquema, es decir los nombres, tipos y llaves foráneas, basta para que el modelo entienda cómo unir tablas. Los datos de clientes, montos reales o correos se quedan en tu servidor. Además, conviene decirle al modelo qué dialecto usas (PostgreSQL, MySQL, SQL Server), porque LIMIT, TOP y las funciones de fecha cambian entre motores.

2. Solo lectura por defecto (y de verdad)

La primera línea de defensa no es el prompt: es la base de datos. Conéctate con un usuario que solo tenga permisos de lectura:

CREATE ROLE copilot_reader LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE app TO copilot_reader;
GRANT USAGE ON SCHEMA public TO copilot_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO copilot_reader;

-- Extra en PostgreSQL: toda transacción será de solo lectura
ALTER ROLE copilot_reader SET default_transaction_read_only = on;

Si algo falla en las capas de arriba, el motor igual rechaza cualquier escritura. En SQL Copilot esto se refleja en el Safe Mode, que fuerza la ejecución solo-SELECT y viene activado por defecto. Desactivarlo requiere una confirmación explícita.

La base de datos con un usuario de solo lectura acepta SELECT y rechaza UPDATE o DELETE

3. Valida el SQL con un parser, no con regex

Es tentador revisar si la consulta “empieza con SELECT”. No alcanza: un WITH ... DELETE o varias sentencias separadas por ; se te escapan fácilmente. Usa un parser de SQL y revisa el árbol:

import { Parser } from 'node-sql-parser';

const parser = new Parser();

export function assertReadOnly(sql: string, database = 'PostgresQL') {
  const ast = parser.astify(sql, { database });
  const statements = Array.isArray(ast) ? ast : [ast];

  if (statements.length !== 1) {
    throw new Error('Solo se permite una sentencia por consulta.');
  }
  if (statements[0].type !== 'select') {
    throw new Error(`Sentencia no permitida: ${statements[0].type}`);
  }
}

Si el parser no puede interpretar la consulta, recházala. Es mejor pedirle al usuario que reformule que ejecutar algo que no entiendes.

Un DELETE escondido en un CTE engaña a un regex, pero un parser de SQL lo detecta y rechaza la consulta

4. Pon límites al costo

Una consulta de solo lectura también puede hacer daño: un SELECT * sin filtro sobre una tabla de 200 millones de filas puede tumbar una réplica. Mis reglas:

  • Exigir LIMIT (o TOP en SQL Server) en las consultas generadas.
  • Timeout por sentencia, por ejemplo SET statement_timeout = '15s' en PostgreSQL.
  • Paginación en la interfaz, en lugar de traer todo de golpe.

5. El humano revisa antes de ejecutar

La IA propone y la persona decide. Mostrar el SQL generado, permitir editarlo y que el usuario presione “Ejecutar” cambia completamente el modelo de riesgo. También es una oportunidad de aprendizaje: una función para explicar en lenguaje claro qué hace una consulta ayuda a que el usuario confíe en lo que va a correr.

6. Cuida las credenciales

Una herramienta que se conecta a bases de datos guarda cadenas de conexión, y eso es material sensible:

  • Nunca guardes contraseñas en texto plano. Cífralas localmente (por ejemplo con AES-256-GCM) o usa el llavero del sistema operativo.
  • No muestres secretos completos en la interfaz.
  • No envíes credenciales al modelo, nunca.

7. Deja rastro

Guarda un historial de qué se pidió, qué SQL se generó y qué se ejecutó. Cuando algo sale raro, ese historial es lo único que te permite entender qué pasó.

Resumen

CapaQué evita
Esquema sin datosFiltrar información sensible al modelo
Usuario de solo lecturaEscrituras accidentales, aunque falle todo lo demás
Validación con parserSentencias peligrosas camufladas
LIMIT y timeoutsConsultas que tumban la base de datos
Revisión humanaEjecutar algo que nadie entendió
Credenciales cifradasFugas de acceso

La IA es excelente escribiendo SQL, pero la seguridad no puede depender de que el modelo se porte bien. Tiene que estar en la arquitectura.

Comparto más tips de desarrollo e IA en TikTok: @andydev22.