Segurança do código gerado por IA: cuidados essenciais
Entenda por que sugestões de IA podem trazer vulnerabilidades silenciosas e aprenda a revisar esse código com método antes de levá-lo para produção com segurança.

Assistentes de código como Copilot e Cursor aumentaram a produtividade de muita gente, mas eles não foram treinados para escrever código seguro — foram treinados para escrever código plausível. Essa diferença é a origem de uma classe inteira de bugs que passam despercebidos no commit e só aparecem quando alguém os explora. Neste artigo você vai entender por que isso acontece e o que fazer para revisar com método.
Por que a IA gera código inseguro
Modelos de geração de código aprendem com bilhões de linhas de repositórios públicos. O problema é que boa parte desse material contém más práticas: concatenação de SQL, segredos hardcoded, validação ausente e bibliotecas desatualizadas. O modelo replica esses padrões porque eles são estatisticamente comuns, não porque são corretos.
O estudo clássico de Pearce e colegas (2022) investigou exatamente isso. Os autores submeteram o GitHub Copilot a uma bateria de cenários de programação relevantes para segurança e encontraram que cerca de 40% dos programas gerados continham vulnerabilidades em pelo menos uma das completions sugeridas. A conclusão é direta: a ferramenta é útil, mas não é um revisor de segurança.
Há razões técnicas por trás disso que vale entender:
- O modelo otimiza plausibilidade, não correção. Ele prevê o próximo token mais provável dado o contexto, e código inseguro frequentemente é o mais "provável" porque é o mais comum nos dados de treino.
- Ele não tem o contexto de ameaça da sua aplicação. O modelo não sabe que aquele parâmetro vem de um usuário não autenticado ou que aquele endpoint manipula dados financeiros.
- Dados de treino envelhecem. Uma versão de biblioteca que era segura quando o modelo foi treinado pode ter CVEs descobertos depois — e o modelo continua sugerindo aquela versão.
- Exemplos didáticos viram produção. Muito código público é tutorial, que omite tratamento de erro e validação por brevidade. O modelo aprende esses atalhos como se fossem o padrão.
Há ainda um efeito psicológico. Quando o código aparece pronto, formatado e com aparência profissional, a tendência é confiar. Esse viés de automação reduz o escrutínio justamente no momento em que ele seria mais necessário. Estudos de fatores humanos mostram que revisamos com menos rigor aquilo que uma máquina produz com aparência de autoridade — e o código de IA é o exemplo perfeito.
As vulnerabilidades mais comuns em sugestões de IA
Os padrões inseguros que mais aparecem em código gerado se sobrepõem fortemente às falhas catalogadas no OWASP Top 10 explicado: as 10 maiores falhas de segurança web. Vale conhecer os suspeitos de sempre:
- Injeção de SQL: quando a IA monta a query concatenando strings com input do usuário em vez de usar consultas parametrizadas.
- Segredos hardcoded: chaves de [API](/seguranca-de-chaves-api), senhas e tokens escritos diretamente no código de exemplo.
- Validação de entrada ausente: rotas que confiam cegamente no corpo da requisição.
- Criptografia fraca ou mal usada: algoritmos obsoletos (MD5, SHA1 para senhas) ou modos inseguros.
- Controle de acesso quebrado: endpoints que não verificam se o usuário tem permissão para o recurso solicitado.
- Dependências vulneráveis: a IA sugere uma biblioteca com versão conhecida por ter CVEs abertos.
- Path traversal e injeção de comando: quando input do usuário é usado para montar caminhos de arquivo ou comandos de shell sem sanitização.
- Tratamento de erro que vaza informação: blocos catch que devolvem a mensagem crua do driver ao cliente.
Exemplo concreto: injeção de SQL
Veja uma sugestão típica que parece correta, mas é perigosa:
# INSEGURO — sugestão comum de IA
def buscar_usuario(email):
query = f"SELECT * FROM usuarios WHERE email = '{email}'"
return db.execute(query)O input email entra direto na string. Um atacante envia ' OR '1'='1 e a lógica da consulta é subvertida. A versão segura usa parâmetros:
# SEGURO — consulta parametrizada
def buscar_usuario(email):
query = "SELECT * FROM usuarios WHERE email = %s"
return db.execute(query, (email,))A diferença é sutil no diff, mas é a fronteira entre uma aplicação saudável e um vazamento de banco de dados.
Exemplo concreto: injeção de comando
Outro padrão recorrente é montar um comando de sistema concatenando entrada do usuário:
import os
# INSEGURO — input direto no shell
def converter(nome_arquivo):
os.system(f"convert {nome_arquivo} saida.png")Se nome_arquivo for foto.jpg; rm -rf /, o shell executa os dois comandos. A versão segura evita o shell e passa argumentos como lista, sem interpolação:
import subprocess
# SEGURO — sem shell, argumentos separados
def converter(nome_arquivo):
subprocess.run(["convert", nome_arquivo, "saida.png"], check=True)A diferença é não deixar o usuário influenciar a estrutura do comando, apenas os dados. Esse é o princípio comum a toda defesa contra injeção: separar código de dados.
O risco específico do vibe coding
O fenômeno do vibe coding: o que é e como construir apps só com prompts amplifica esses riscos. Quando você aceita grandes blocos de código sem ler — confiando apenas na "vibe" de que funciona — você abre mão da única camada de revisão que ainda restava: você mesmo.
Aplicações construídas inteiramente por prompt costumam acumular dívidas de segurança invisíveis: autenticação implementada pela metade, regras de autorização inexistentes e endpoints internos expostos. O código roda, a demo funciona, e a falha fica dormente até a primeira sondagem maliciosa.
O perigo se agrava porque vulnerabilidades de autorização e lógica de negócio não quebram nenhum teste funcional. Um endpoint que devolve os dados de qualquer usuário quando você troca o ID na URL funciona perfeitamente do ponto de vista da demo — ele só está "funcionando errado" sob a ótica de um atacante. Como o vibe coding foca em "fazer funcionar", essa classe de falha passa totalmente despercebida.
A regra prática é simples: quanto menos você lê o código, mais rígido precisa ser o seu pipeline automatizado de verificação. Vibe coding sem testes e sem scanners é uma aposta.
Prompt injection: quando a entrada vira código
Há uma categoria de risco que é nova e específica de aplicações com IA. Se o seu app passa input do usuário diretamente para um LLM que toma decisões ou executa ações, você precisa entender prompt injection: a injeção de comandos que ameaça apps de IA. Nesse cenário, o "código" perigoso é o próprio texto do usuário, manipulando o comportamento do modelo.
Isso significa que revisar o código gerado não basta — é preciso revisar também como o código trata as entradas que chegam ao modelo. As duas frentes se complementam. Um agente de IA com acesso a ferramentas (executar SQL, enviar e-mail, chamar APIs) é especialmente perigoso: uma instrução maliciosa embutida no texto do usuário pode fazer o agente abusar dessas ferramentas. A defesa exige tratar a saída do LLM como não confiável e aplicar autorização fora do modelo, na fronteira de cada ferramenta.
Como revisar código gerado por IA
A boa notícia é que técnicas tradicionais de revisão funcionam bem aqui. O segredo é tratar a IA como um desenvolvedor júnior talentoso porém descuidado: aproveite a velocidade, mas confira tudo. As práticas de code review eficiente: como revisar código sem brigar com o time se aplicam integralmente.
O ponto mental mais importante é o que mudou no equilíbrio do trabalho: com IA, escrever ficou barato e revisar ficou o gargalo. Sua atenção deve migrar de produzir linhas para auditar intenções. Pergunte-se sempre, ao ler um bloco gerado: de onde vêm os dados que isto manipula, e o que acontece se forem maliciosos?
Checklist de revisão de segurança
- Toda entrada externa é validada e sanitizada? Corpo de requisição, query params, headers, uploads.
- As consultas ao banco são parametrizadas? Nenhuma string de SQL concatenada com input.
- Existe algum segredo no código? Chaves, senhas e tokens devem vir de variáveis de ambiente ou cofres.
- A autorização é verificada no servidor? Nunca confie no cliente para decidir permissões, e confira se cada rota by-id valida que o recurso pertence ao usuário.
- As dependências sugeridas estão atualizadas e sem CVEs conhecidos?
- Erros internos não vazam para o cliente? Mensagens de driver e stack traces são informação para o atacante.
- A criptografia usa algoritmos atuais? Senhas com bcrypt/argon2, TLS para tráfego.
- Comandos de sistema e caminhos de arquivo são montados sem interpolar input? Nada de os.system(f"...") nem concatenar caminhos do usuário.
Ferramentas automatizadas que ajudam
A revisão humana é essencial, mas não escala sozinha. Combine-a com ferramentas:
- SAST (Static Application Security Testing): ferramentas como Semgrep e CodeQL leem o código e apontam padrões inseguros antes do merge.
- Scanners de dependência (SCA): npm audit, pip-audit, Dependabot e Snyk alertam sobre bibliotecas vulneráveis.
- Detecção de segredos: gitleaks e truffleHog bloqueiam commits que contêm chaves.
- Linters de segurança: regras específicas no ESLint, Bandit (Python) ou gosec (Go).
- DAST (Dynamic Application Security Testing): ferramentas como OWASP ZAP) testam a aplicação rodando, pegando falhas que a análise estática não vê.
O ideal é que esses scanners rodem no CI, falhando o build quando encontram algo crítico. Assim a verificação não depende de alguém lembrar de fazê-la. Um exemplo conceitual de um passo de CI que barra o merge ao detectar problema:
# trecho ilustrativo de pipeline
seguranca:
steps:
- run: semgrep --config=auto --error # SAST falha o build em achado
- run: pip-audit # dependências vulneráveis
- run: gitleaks detect --no-banner # segredos vazadosVale entender o trade-off dessas ferramentas: SAST gera falsos positivos (aponta o que não é problema) e também falsos negativos (deixa passar o que é). Por isso elas complementam, mas não substituem, o olho humano. Ajustar regras para reduzir ruído é parte de manter o pipeline útil — um scanner que grita demais acaba ignorado.
Dependências: o risco que a IA herda e amplia
Um vetor que merece destaque próprio é o das dependências. A IA frequentemente sugere instalar bibliotecas para resolver um problema, e aí moram dois perigos distintos. O primeiro é sugerir uma versão com vulnerabilidade conhecida — algo que um scanner de dependências pega. O segundo é mais insidioso: a alucinação de pacotes.
Modelos às vezes inventam nomes de bibliotecas que não existem, mas que soam plausíveis. Atacantes monitoram esses nomes alucinados recorrentes e publicam pacotes maliciosos com exatamente esses nomes — uma técnica apelidada de slopsquatting. Quem instala cegamente o que a IA sugeriu acaba importando código hostil. A defesa é simples mas exige disciplina: confira que o pacote existe, é mantido e é o oficial antes de instalar. Desconfie especialmente de bibliotecas que você nunca ouviu falar e que aparecem só porque a IA citou.
# Antes de instalar o que a IA sugeriu, verifique:
npm view <pacote> # existe? quem mantém? última publicação?
npm view <pacote> versions # quais versões? a sugerida é real?Esse cuidado se soma ao princípio de tratar toda dependência como superfície de ataque: cada biblioteca importada é código de terceiros rodando com os privilégios da sua aplicação.
Integrando segurança no fluxo com IA
A meta não é abandonar os assistentes de código — é usá-los com consciência. Se você está adotando ferramentas como as descritas no GitHub Copilot: guia completo para programar com IA, inclua a segurança no fluxo desde o início.
Algumas táticas práticas:
- Peça o código seguro explicitamente. Prompts como "use consultas parametrizadas e valide a entrada" mudam a saída do modelo.
- Dê contexto de ameaça no prompt. Diga ao modelo que o input vem de usuário não autenticado e que o endpoint é público; a saída muda.
- Gere os testes junto com o código. Inclua casos de borda e de abuso, não só o caminho feliz.
- Use a própria IA para revisar. Peça ao modelo para auditar o trecho procurando vulnerabilidades — ele costuma ser melhor crítico do que autor.
- Mantenha o ser humano no loop dos pontos sensíveis. Autenticação, autorização, pagamentos e manipulação de dados pessoais merecem revisão manual sempre.
- Padronize com regras de projeto. Arquivos de instrução (estilo .cursorrules ou equivalente) que orientam o assistente a sempre parametrizar SQL e nunca hardcodar segredos elevam o piso de todas as gerações.
Erros comuns ao confiar na IA
- Aceitar a primeira sugestão sem ler. A velocidade é o atrativo e a armadilha ao mesmo tempo.
- Assumir que "compila e passa nos testes" significa seguro. Falhas de autorização e lógica não quebram testes funcionais.
- Copiar a biblioteca que a IA citou sem checar a versão. O modelo pode sugerir uma versão com CVE conhecido.
- Confiar no código de exemplo de configuração. Exemplos costumam vir com debug=True, CORS aberto e segredos placeholder que viram produção.
- Esquecer que o modelo não conhece o seu modelo de ameaça. Só você sabe o que é sensível na sua aplicação.
Perguntas frequentes
Devo parar de usar Copilot e similares por causa disso? Não. O ganho de produtividade é real e os riscos são gerenciáveis. A resposta certa não é abandonar a ferramenta, e sim emparelhá-la com revisão e automação. Trate-a como um acelerador que exige supervisão, não como um autopilô.
A IA não consegue revisar a própria segurança? Em parte. Pedir ao modelo para auditar o código que ele mesmo gerou costuma revelar problemas, porque criticar é uma tarefa diferente de criar. Mas isso não substitui SAST, scanners de dependência e revisão humana nos pontos sensíveis — é mais uma camada, não a única.
Qual a vulnerabilidade mais comum em código de IA? As injeções (SQL, comando) e os segredos hardcoded lideram, justamente porque são padrões abundantes nos dados de treino. Felizmente também são das mais fáceis de pegar com ferramentas automatizadas e com um checklist disciplinado.
Code review humano ainda vale a pena se eu tenho scanners? Sim, e mais do que nunca. Scanners pegam padrões conhecidos; falhas de lógica de negócio e de autorização — as mais perigosas em apps gerados por IA — exigem entendimento humano do que a aplicação deveria fazer.
Conclusão
Código gerado por IA é um acelerador poderoso, mas vem com uma responsabilidade renovada de revisão. O estudo de Pearce e colegas (2022) deixa claro que uma fração relevante das sugestões traz falhas reais, e essas falhas mapeiam diretamente para as categorias do OWASP Top 10 (OWASP Foundation, 2021). A defesa não é desconfiar de tudo nem confiar em tudo — é montar um processo: prompts que pedem segurança e dão contexto de ameaça, revisão humana nos pontos críticos, e ferramentas automatizadas no CI cobrindo o resto. Lembre-se de que o trabalho mudou de lugar: escrever ficou barato, revisar virou o ofício. Trate a IA como um colega rápido que precisa de um bom revisor ao lado, e você ganha a velocidade sem herdar os riscos.
Referências
- Pearce, H., Ahmad, B., Tan, B., Dolan-Gavitt, B., Karri, R. (2022). Asleep at the Keyboard? Assessing the Security of GitHub Copilot's Code Contributions. IEEE S&P.
- OWASP Foundation (2021). OWASP Top 10:2021. OWASP.
- OWASP Foundation (2023). OWASP Top 10 for Large Language Model Applications. OWASP.


