Pular para o conteúdo principal

Mentalidade de Segurança para Vibe Coding com o Cursor

Vibe coding security with Cursor

O vibe coding com o Cursor é rápido e divertido: você descreve o que quer, o agente escreve o código, e você mantém o fluxo sem parar para ler cada linha. O problema é que um modelo treinado para satisfazer prompts vai alegremente produzir código que funciona e parece limpo, ainda carregando injeção de SQL, chaves de API codificadas diretamente ou uma verificação de autorização que confia no cliente. Este guia dá a você uma mentalidade de segurança repetível para o fluxo de trabalho de vibe coding no Cursor, desde detectar as falhas que o código gerado por IA produz com mais frequência até auditar a saída, aplicar regras, lidar com segredos e executar uma checklist pré-implantação antes que qualquer coisa seja entregue.

O Que É Vibe Coding e Por Que a Segurança Importa​

Vibe coding significa que você conduz com intenção e deixa o modelo preencher os detalhes de implementação. Você pede "um endpoint de login com um cookie de sessão" e o Cursor escreve a rota, a chamada ao banco de dados e o middleware. A saída geralmente é funcional, mas funcional não é o mesmo que seguro. O modelo otimiza para fazer o código rodar, não para se defender de um atacante que lê o mesmo código.

A segurança importa mais neste fluxo de trabalho, não menos, por três motivos:

  • Você revisa menos. Quando o modelo escreve dezenas de linhas por prompt, a etapa de revisão humana se torna o gargalo e acaba sendo pulada.
  • O modelo aprendeu com código inseguro. Repositórios públicos contêm bastante SQL concatenado em strings e armazenamento de tokens no lado do cliente, então esses padrões aparecem nas completions.
  • O raio de impacto é grande. O modo Agent pode tocar muitos arquivos de uma vez, então um único padrão inseguro é duplicado por toda a base de código.

A segurança não é uma fase separada que você acrescenta no final. É um hábito constante que você incorpora à forma como você escreve prompts, revisa e implanta com o Cursor.

Você ainda é o revisor

O agente do Cursor pode escrever código que compila, passa nos seus testes e ainda entrega uma vulnerabilidade. O modelo não pode assumir a responsabilidade pelo seu risco. Trate cada bloco gerado como um rascunho de um desenvolvedor júnior rápido, mas descuidado.

As Vulnerabilidades Mais Comuns em Código Gerado por IA​

Estes são os modos de falha que aparecem repetidamente em código gerado por IA. Aprenda a reconhecer a forma de cada um para poder detectá-lo ao escanear, em vez de ler cada linha.

VulnerabilidadeComo ela aparece no código geradoPor que o modelo a produz
Injeção de SQLConsulta construída com concatenação de strings em vez de parâmetrosA concatenação é mais simples de escrever e lê "naturalmente"
XSSEntrada de usuário não sanitizada renderizada com innerHTML ou dangerouslySetInnerHTMLRenderizar HTML diretamente é o caminho mais curto para o resultado visual
Segredos/chaves de API codificados diretamenteconst API_KEY = "sk-live-..." deixado no arquivo de código-fonteUm valor literal torna o trecho autossuficiente e executável
Autenticação/sessão quebradaTokens armazenados em localStorage, sem expiração, sem validação no lado do servidorO armazenamento no cliente é o mais fácil de demonstrar em um navegador
CORS permissivoAccess-Control-Allow-Origin: * com credenciaisO curinga é a correção de uma linha que interrompe o erro imediato
Desserialização insegurapickle.loads, eval, ou confiar JSON direto em objetosDesserializar tudo é conveniente e presume entrada confiável
Endpoints de administração expostosRotas como /admin/users sem verificação de papelO modelo implementa o recurso, mas esquece a proteção
Validação de entrada ausenteCorpo usado diretamente na lógica sem verificação de esquemaA validação parece redundante quando o caminho feliz já funciona

Injeção de SQL em JavaScript​

Aqui está o par clássico: uma consulta que concatena a entrada do usuário, e a mesma consulta escrita com placeholders parametrizados.

// VULNERABLE: user input is concatenated straight into the query
app.get('/user', async (req, res) => {
const sql = "SELECT * FROM users WHERE email = '" + req.query.email + "'";
const rows = await db.query(sql);
res.json(rows);
});
// FIXED: parameterized query, input never becomes part of the SQL string
app.get('/user', async (req, res) => {
const sql = 'SELECT * FROM users WHERE email = $1';
const rows = await db.query(sql, [req.query.email]);
res.json(rows);
});

Verificações de Autorização em Python​

A versão vulnerável confia em uma flag enviada pelo cliente. A versão corrigida verifica a sessão autenticada no servidor.

# VULNERABLE: the client tells the server whether it is an admin
@app.get("/admin/users")
def admin_users(request):
if request.args.get("is_admin") == "true":
return db.list_users()
return {"error": "forbidden"}
# FIXED: the server decides based on the authenticated session
@app.get("/admin/users")
def admin_users(request):
user = request.session.get("user")
if not user or user["role"] != "admin":
abort(403)
return db.list_users()

Armazenar um token de sessão em localStorage o torna legível por qualquer script na página, o que transforma um único bug de XSS em tomada de conta completa.

// VULNERABLE: token readable by any script, no protection against XSS
localStorage.setItem('token', response.token);
// FIXED: server sets an httpOnly cookie that scripts cannot read
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 1000 * 60 * 60,
});

Auditando Código Gerado por IA com o Cursor​

Auditar não significa ler cada linha. Significa pedir uma segunda passada do modelo para atacar o que a primeira passada escreveu. O Cursor é bom nisso quando você lhe dá a persona certa e um alvo claro.

Um Prompt de Persona de Revisão de Segurança​

Reutilize este prompt no modo Agent depois que o agente terminar um recurso. Ele força o modelo a mudar de construtor para atacante.

You are a security reviewer auditing code a previous agent just wrote.
For every file changed in this feature:
1. List each place that handles user input, authentication, or secrets.
2. For each, describe the concrete attack an unauthenticated user could run.
3. Show the vulnerable line and the fixed line side by side.
4. Rate severity as low, medium, high, or critical with one sentence of reasoning.
Do not rewrite the whole feature. Report findings only.

Pedindo Modelagem de Ameaças no Modo Agent​

Além de revisar o código finalizado, você pode pedir ao Cursor para modelar ameaças antes de escrever. Isso revela falhas de design que a revisão em nível de linha deixa passar.

Before implementing this billing endpoint, do a short threat model:
- Who can call this route and with what privileges?
- What happens if the amount, currency, or user ID is tampered with?
- Which trust boundary does this input cross?
Then implement the endpoint with those mitigations in place.

Usando .cursor/rules para Aplicar Verificações​

Você pode codificar a própria auditoria como uma regra para que o agente a execute automaticamente a cada mudança. Regras em .cursor/rules/ são carregadas no contexto, o que significa que a checklist de segurança está sempre presente quando o agente trabalha.

Audite após cada sessão do agente

Faça de "executar uma revisão de segurança" um reflexo após qualquer sessão do agente que toque em autenticação, tratamento de entrada ou segredos. Custa um prompt e capta os modos de falha da tabela acima antes que eles cheguem a um commit.

Aplicando Segurança com Regras do Cursor​

Um arquivo de regras do projeto mantém suas expectativas de segurança à frente do modelo sem você se repetir. Crie .cursor/rules/security.mdc e faça commit dele para que cada colaborador receba os mesmos guardrails.

---
description: Security requirements for all code in this project
globs: ["**/*.ts", "**/*.tsx", "**/*.py", "**/*.js"]
alwaysApply: true
---

# Security requirements

- Never write SQL by concatenating user input. Use parameterized queries or an ORM.
- Never render user input with innerHTML or dangerouslySetInnerHTML. Escape it or use a safe renderer.
- Never hardcode secrets, API keys, or tokens. Read them from environment variables.
- Never grant administrative access based on a value supplied by the client.
- Always validate request bodies against a schema before use.
- Always set httpOnly, secure, and sameSite on session cookies.
- Never use eval, exec, or pickle.loads on untrusted input.
- When adding a new endpoint, state explicitly which role can call it.
- When touching auth or input handling, add a security-review note in the response.

Com alwaysApply: true, a regra é injetada em toda requisição, então o agente se autocorrige enquanto escreve, em vez de você capturar problemas depois. Você também pode delimitar regras a pastas específicas com globs para que regras de segurança de backend não poluam o trabalho de frontend.

Gerenciando Segredos e Variáveis de Ambiente​

Segredos codificados diretamente são a vulnerabilidade mais fácil de entregar por acidente, porque um valor literal torna o código executável e o modelo gosta de código executável. Mantenha os segredos totalmente fora do código-fonte.

Padrões de Variáveis de Ambiente​

# .env.example -- commit this, it documents the shape without real values
DATABASE_URL=postgres://user:password@localhost:5432/app
SESSION_SECRET=replace-me
STRIPE_SECRET_KEY=replace-me
# Load locally, never commit the real .env
cp .env.example .env
# .env -- gitignored, contains the real values and stays on your machine
DATABASE_URL=postgres://realuser:realpass@prod-host:5432/app
SESSION_SECRET=a-long-random-value-from-a-secret-manager
STRIPE_SECRET_KEY=sk_live_the_actual_key

Sempre adicione .env e arquivos relacionados ao .gitignore antes do seu primeiro commit, não depois.

# .gitignore
.env
.env.local
.env.*.local
*.pem
*.key

Escaneamento de Segredos Pré-Commit com gitleaks​

Um hook pré-commit impede um segredo antes que ele chegue ao histórico do repositório, o que é muito mais barato do que rotacionar uma chave vazada.

# Install gitleaks (Homebrew example)
brew install gitleaks

# Scan the whole repo once
gitleaks detect --source . --verbose

# Or install it as a pre-commit hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
pre-commit install
pre-commit run --all-files
Rotacione primeiro, limpe depois

Se um segredo real chegar a um commit, rotacione a credencial imediatamente. Excluir a linha ou reescrever o histórico não desvaza uma chave que já foi enviada para um remoto.

Validando Autenticação e Autorização Manualmente​

Varreduras automatizadas deixam passar bugs de lógica, então você ainda tem que verificar o fluxo manualmente. Percorra estas verificações contra uma instância em execução.

Verificações de Sessão e Autenticação​

  • Faça logout e depois solicite uma rota protegida com o token antigo. Deve falhar com 401, não retornar dados.
  • Altere seu próprio ID de usuário em uma URL ou corpo para o ID de outro usuário. O servidor deve rejeitar, não confiar no parâmetro.
  • Chame uma rota de administração como não administrador. Deve retornar 403, independentemente de qualquer flag fornecida pelo cliente.
  • Confirme que os cookies são definidos com httpOnly, secure e uma expiração. Abra o console do navegador e tente ler o cookie com document.cookie; ele não deve aparecer.
  • Redefina uma senha e verifique se a sessão antiga é invalidada.

Verificações de Token e Cabeçalho​

# A protected route without credentials must not return data
curl -i https://api.example.com/me

# A protected route with a forged or expired token must fail
curl -i https://api.example.com/me -H "Authorization: Bearer forged.token.value"

# An admin route as a normal user must return 403
curl -i https://api.example.com/admin/users -H "Authorization: Bearer $USER_TOKEN"

Execute estas verificações contra um ambiente de staging que espelhe a produção, não apenas contra localhost onde os dados são descartáveis.

Escaneamento de Dependências e Cadeia de Suprimentos​

Seu próprio código é apenas parte da superfície. Código gerado puxa dependências, e um pacote vulnerável é uma vulnerabilidade que você entregou. Escaneie a árvore a cada mudança.

# Node / npm -- built in, no extra install
npm audit
npm audit --production

# Python
pip-audit
pip-audit -r requirements.txt
# Snyk for a cross-language view
npm install -g snyk
snyk test
snyk monitor

# Or run the Snyk CLI against a Python project
pip install snyk
snyk test --file=requirements.txt

Conecte um scanner ao CI para que ele rode em cada pull request em vez de depender da memória.

# .github/workflows/security.yml (excerpt)
- name: Scan dependencies
run: npm audit --production --audit-level=high
Fixe e revise novas dependências

Antes de aceitar um pacote que um modelo sugeriu, verifique sua contagem de downloads, data da última publicação e mantenedor. Pacotes com typosquatting são um vetor de ataque real, e um modelo pode sugerir um nome próximo, mas que não é a biblioteca real.

Uma Checklist de Segurança Pré-Implantação​

Execute esta lista antes de cada implantação. Ela é deliberadamente curta para que você realmente a use.

  • Nenhum segredo, chave de API ou token codificado diretamente em qualquer arquivo comitado
  • .env e arquivos de chave estão no .gitignore e nunca foram comitados
  • Todo SQL usa consultas parametrizadas ou um ORM, sem concatenação de strings
  • Toda entrada de usuário é validada contra um esquema antes do uso
  • Tokens de sessão são armazenados em cookies httpOnly, não em localStorage
  • Cada rota protegida retorna 401/403 quando não autenticada ou não autorizada
  • Rotas de administração verificam o papel no lado do servidor, nunca a partir de um valor fornecido pelo cliente
  • O CORS permite apenas suas origens conhecidas, não * com credenciais
  • npm audit / pip-audit não relata nenhuma descoberta alta ou crítica
  • Respostas de erro não vazam stack traces, SQL ou caminhos internos
  • O logging não registra tokens, senhas ou corpos de requisição completos
  • Uma passada de revisão de segurança foi executada em cada arquivo que o agente tocou nesta sprint

Relatando e Remediando Descobertas​

Descobertas só são úteis se forem corrigidas e rastreadas. Dê a cada uma apenas estrutura suficiente para agir.

  1. Registre a descoberta. Uma linha com o arquivo, a linha, o tipo de vulnerabilidade e a severidade.
  2. Escreva a correção como um diff. Peça ao Cursor para produzir a mudança de linha-vulnerável-para-linha-corrigida, depois revise-a.
  3. Adicione um teste que prove a correção. Um teste de regressão para a injeção ou o bypass de autenticação evita que ele volte.
  4. Atualize a regra. Se o agente produziu o bug, adicione uma linha a .cursor/rules/security.mdc para que ele não o produza novamente.
  5. Reteste e feche. Rode novamente as verificações manuais e o scanner contra a build corrigida.
Finding: CWE-89 SQL injection, src/api/users.ts:42, severity high
Vulnerable: db.query("SELECT * FROM users WHERE id = " + id)
Fixed: db.query("SELECT * FROM users WHERE id = $1", [id])
Regression test: request /user?id=1' OR '1'='1 returns no rows
Rule update: added "never concatenate user input into SQL" to security.mdc
Status: fixed, verified in staging

Trate o arquivo de regras como um registro crescente de cada erro que o agente cometeu. Com o tempo, ele se torna o ativo de segurança mais valioso do repositório, porque codifica exatamente as maneiras pelas quais sua base de código já esteve errada antes.