Blog do SQLFix

Linguagem natural para SQL: verificações de segurança que importam

Traduzir uma pergunta em SQL é genuinamente útil para os primeiros rascunhos, mas a execução automática da saída do modelo em um banco de dados ao vivo é como um erro de digitação se torna uma tabela excluída. A postura segura é somente rascunho: o modelo propõe, um humano ou um pipeline protegido decide. O SQLForm segue esse modelo somente de rascunho e nunca executa o que gera sem uma etapa de revisão.

1. Comece com um pipeline somente de rascunho

Construí o SQLForm em torno da ideia de que um modelo de linguagem é um assistente de rascunho, não um mecanismo de execução. Quando um usuário pede “mostre-me as vendas do mês passado por região”, o modelo retorna uma instrução SELECT. Essa instrução é colocada em uma fila de rascunho e nunca é enviada para o conector de produção. Um operador humano ou um portão automatizado deve aprovar explicitamente o rascunho antes que ele saia da fila. Essa separação cria um registro de auditoria claro: toda consulta que toca os dados tem uma etapa de aprovação registrada.

A fila de rascunho é persistida em uma tabela leve com colunas para ID do usuário, carimbo de data e hora, prompt bruto, SQL gerado e status de aprovação. Se o sistema travar, a fila pode ser reproduzida e nenhuma consulta é perdida. Escolhi esse design após ver equipes perderem dados porque um script de demonstração executou uma instrução DELETE que o modelo pensou que era uma SELECT. A fila impede essa classe de erro por construção.

2. Detectar instruções mutantes antes que elas atinjam a fila

A primeira linha de defesa é uma varredura lexical simples. Procuro pelos tokens DELETE, UPDATE, INSERT, DROP, TRUNCATE, ALTER, GRANT, REVOKE e CREATE. Se algum deles aparecer, o rascunho é marcado como mutante e colocado em um estado de espera que exige aprovação explícita de um revisor sênior. Mesmo um SELECT que contém uma cláusula WITH que chama uma função mutante é capturado porque a varredura é realizada na string SQL bruta antes de qualquer análise.

Também verifico separadores de instruções. Um ponto e vírgula seguido por outro token significa que o usuário pode estar tentando encadear operações. Por exemplo, “SELECT * FROM users; DELETE FROM logs” passaria por uma verificação ingênua de apenas SELECT, mas é bloqueado porque o scanner vê o token DELETE após o primeiro ponto e vírgula.

Para evitar falsos positivos em nomes de objetos legítimos (como uma coluna nomeada “delete_flag”), o scanner trata os tokens como palavras-chave apenas quando eles são seguidos por espaço em branco ou parênteses, correspondendo à gramática SQL típica. Isso mantém a regra precisa sem banir identificadores úteis.

3. Executar rascunhos em um sandbox somente leitura

Uma vez que um rascunho é aprovado, eu o executo contra uma réplica somente leitura do esquema de produção. A réplica é atualizada diariamente a partir do primário e não tem privilégios de gravação atribuídos à função de conexão usada pelo SQLForm. Se o modelo produzir alguma instrução de mutação, o banco de dados retornará um erro e nenhum dado será alterado. Isso transforma um erro potencialmente caro em uma falha visível que o revisor pode ver imediatamente.

Eu também forço a propriedade somente leitura no nível de conexão definindo o parâmetro do PostgreSQL default_transaction_read_only = on para a sessão. No MySQL, eu inicio a sessão com SET SESSION TRANSACTION ISOLATION LEVEL READ ONLY;. Essas configurações fazem com que o servidor rejeite qualquer comando WRITE, ALTER ou DROP com um código de erro claro, que eu exibo na interface do usuário.

Usar uma réplica tem um benefício adicional: a consulta pode ser executada contra dados ligeiramente desatualizados sem afetar os SLAs, e eu posso monitorar o comprimento da consulta e o uso de recursos sem arriscar a carga de produção. Se a réplica atrasar além de um limiar configurável, o sistema mantém o rascunho e notifica o aprovador de que os dados podem estar desatualizados.

4. Parametrizar consultas aprovadas

Depois que um rascunho passa pela etapa de revisão humana, eu movo o SQL para a camada de aplicação como uma instrução preparada. O texto gerado pelo modelo se torna um modelo onde qualquer valor fornecido pelo usuário é vinculado como parâmetro. Eu nunca concateno strings brutos, mesmo após a aprovação, porque isso reintroduz o risco de injeção que o rascunho pretendia evitar.

Eu sigo as orientações do OWASP SQL Injection Prevention Cheat Sheet, que lista as instruções preparadas como a principal defesa. Na prática, eu uso a API nativa do driver de banco de dados: pg_prepare / pg_execute para PostgreSQL e PreparedStatement para MySQL. O driver cuida da escape e da segurança de tipo, então um valor que contém uma aspa ou um ponto e vírgula é tratado como dado, não como SQL.

Para garantir que o modelo permaneça válido, eu analiso o SQL aprovado com um analisador SQL leve (como sqlparse) e verifico que cada espaço reservado corresponde a um índice de parâmetro. Se o analisador encontrar um token solto que pareça um valor, mas não esteja vinculado, o rascunho é enviado de volta para revisão.

5. Revisão humana como ponto de controle

O revisor não é esperado para ser um especialista em SQL; a lista de verificação é deliberadamente curta. Primeiramente, eles verificam se a lista de seleção do SELECT corresponde às colunas mencionadas no prompt original. Se o usuário solicitou "receita total por região" e a consulta retorna COUNT(*), o revisor sinaliza isso. Em segundo lugar, eles confirmam que nenhum token de mutação aparece, confiando no scanner, mas também fazendo uma varredura visual para qualquer obfuscção astuta. Em terceiro lugar, eles olham para a cláusula WHERE para ver se ela inadvertidamente puxa mais linhas do que o pretendido (por exemplo, faltando um filtro de data que causaria uma varredura completa da tabela).

Trato a consulta gerada como uma sugestão, exatamente como eu faria ao revisar SQL escrito por um novo colega de equipe. O revisor pode aprovar, solicitar uma alteração ou rejeitar. Se eles solicitarem uma alteração, podem editar o rascunho diretamente na interface do usuário; a versão editada se torna um novo rascunho e passa pelas mesmas verificações novamente. Esse loop garante que a saída do modelo nunca seja a autoridade final.

O ganho de velocidade vem da eliminação da necessidade de escrever SQL de boilerplate do zero. Eu vi equipes reduzir o tempo para produzir uma consulta de primeira versão de dez minutos para menos de dois minutos, enquanto ainda mantêm um humano no loop para segurança.

O que isso não é

Eu não afirmo que o SQLForm captura todos os possíveis erros. O scanner léxico pode ser evitado por codificações incomuns ou por usar sintaxe específica do banco de dados que se assemelha a um comentário. Um atacante determinado poderia criar uma solicitação que faça o modelo produzir uma declaração que use um procedimento armazenado com efeitos colaterais, que o scanner não sinalizaria porque o nome do procedimento não está em nossa lista de palavras-chave. A réplica somente leitura protege contra gravações acidentais, mas se a réplica estiver mal configurada e herdar direitos de gravação, a rede de segurança some.

A revisão humana é essencial, mas os revisores podem perder erros lógicos sutis, especialmente quando a consulta é longa ou usa funções de janela complexas. O processo reduz o risco, mas não o elimina. Eu trato o SQLForm como uma ferramenta de redução de risco, não como uma bala de prata.

Referências

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

Perguntas Frequentes

Apenas contra uma réplica somente leitura ou com um portão duro que bloqueia declarações de mutação. Executar automaticamente SQL gerado contra produção é como um erro de rascunho se torna perda de dados. Mantenha o modelo em um papel de proposta somente.

Um rascunho de modelo é uma entrada não confiável até ser revisado, da mesma classe que a injeção de SQL CWE-89. Um papel somente leitura significa que mesmo uma execução acidental não pode alterar os dados de produção.

Não. Uma vez que uma consulta se move para o código da aplicação, use declarações preparadas. Concatenar valores de volta a uma string reintroduz o risco de injeção. Revisão e parametrização são controles separados.

As declarações DELETE, UPDATE, DROP, TRUNCATE e de concessão de privilégios nunca devem ser executadas em um caminho de execução automática. Elas exigem aprovação explícita e separada.

Tente o produto: SQLForm

2026-09-16 · somente fontes primárias · nenhum tráfego métrico fabricado

Artigos relacionados

Consultas parametrizadas vs SQL gerado

Explore a matriz completa de tópicos de micro-SaaS de IA →

O NL2SQL pode ser seguro para execução automática?

Apenas contra uma réplica somente leitura ou com um portão de controle que bloqueia declarações de mutação. Executar automaticamente SQL gerado contra produção é como um erro de rascunho se torna perda de dados. Mantenha o modelo em um papel de proposta apenas.

Por que um contexto somente leitura é importante? Um rascunho de modelo é uma entrada não confiável até ser revisado, da mesma classe que a injeção de SQL CWE-89. Um papel somente leitura significa que mesmo uma execução acidental não pode alterar os dados de produção. Rever o SQL significa que posso pular a parametrização?

Um rascunho de modelo é uma entrada não confiável até ser revisado, da mesma classe que a injeção de SQL CWE-89. Um papel somente leitura significa que mesmo uma execução acidental não pode alterar os dados de produção.

Quais declarações um camada de segurança deve bloquear por padrão?

As declarações DELETE, UPDATE, DROP, TRUNCATE e de concessão de privilégios nunca devem ser executadas em um caminho de execução automática. Elas exigem aprovação explícita e separada.

Apenas para suporte à decisão. Não é um substituto para a revisão do DBA ou testes de carga de produção. Não há garantias de desempenho.

← Voltar ao blog

Este resultado foi útil?Feedback