Auditoria de Base de Código Lovable: Problemas de Segurança, Lacunas em RLS e Chaves de API Expostas

Um cliente veio até nós no mês passado com um aplicativo gerado pelo Lovable que já estava em produção com clientes pagantes. Eles queriam que adicionássemos alguns recursos e "limpássemos um pouco as coisas." O que encontramos durante nossa auditoria inicial da base de código foi... esclarecedor. Chaves de API expostas em código do lado do cliente. Políticas de Row Level Security ausentes em tabelas contendo dados de usuários. Chamadas diretas ao Supabase sem validação do lado do servidor. E isso não foi um caso isolado -- auditamos seis projetos diferentes do Lovable, e os padrões são notavelmente consistentes.

Deixe-me ser claro: Lovable é uma ferramenta impressionante para prototipagem. A velocidade com que gera UI funcional a partir de prompts em linguagem natural é genuinamente notável. Mas há uma lacuna massiva entre "protótipo funcional" e "aplicação pronta para produção," e essa lacuna está preenchida com vulnerabilidades de segurança que a maioria dos fundadores não-técnicos nem sabe procurar.

Este artigo é um desdobramento técnico do que encontramos em múltiplas auditorias de base de código Lovable. Se você construiu algo com Lovable e está aceitando dados ou dinheiro de usuários reais, você precisa ler isto.

Índice

Lovable Codebase Audit: Security Issues, RLS Gaps, and Exposed API Keys

O que Lovable Realmente Gera

Lovable gera aplicações React (geralmente usando Vite) com Tailwind CSS e componentes shadcn/ui, conectados a um backend Supabase. A stack em si é sólida -- construímos com essas mesmas ferramentas regularmente em nosso trabalho de desenvolvimento Next.js. O problema não são as escolhas de tecnologia. É como elas estão conectadas.

Aqui está uma estrutura típica de projeto Lovable:

src/
├── components/
│   ├── ui/           # componentes shadcn
│   ├── Dashboard.tsx
│   ├── Settings.tsx
│   └── ...
├── integrations/
│   └── supabase/
│       ├── client.ts  # ← É aqui que os problemas começam
│       └── types.ts
├── pages/
├── hooks/
└── lib/

O código gerado é limpo e legível. Dou crédito ao Lovable por isso. Mas legibilidade não é igual a segurança. A arquitetura toma decisões que estão bem para uma demo, mas são perigosas para produção.

Vamos ficar específicos.

O Problema da Chave de API

Cada projeto Lovable que auditamos tinha credenciais Supabase no código do lado do cliente. Agora, antes de os defensores do Supabase aparecerem: sim, a chave anon é projetada para ser pública. A documentação do Supabase explicitamente afirma isso. A chave anon é feita para ser usada em código do lado do cliente, e a segurança é supostamente executada através de políticas de Row Level Security.

Mas é aqui que fica feio.

Em três dos seis projetos que auditamos, encontramos a chave Supabase service_role:

  1. Hardcoded diretamente em um arquivo de utilidade
  2. Armazenada em um arquivo .env que foi commitado no repositório GitHub
  3. Referenciada em uma Supabase Edge Function que era acessível sem autenticação

A chave service_role contorna todas as políticas de RLS. Se alguém conseguir, terá acesso completo de leitura/escrita em todo o seu banco de dados. Todas as tabelas. Todas as linhas. Dados de todos os usuários.

// Padrão real que encontramos em um projeto Lovable (chaves ocultas)
import { createClient } from '@supabase/supabase-js'

// Isto estava em um arquivo chamado lib/admin.ts
// importado e usado em um componente do lado do cliente
const supabaseAdmin = createClient(
  'https://xxxxx.supabase.co',
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' // chave service_role!
)

Isto não foi gerado pelo Lovable diretamente -- foi adicionado pelo fundador quando ele precisava de funcionalidade de admin e pediu ao Lovable para "adicionar um painel de admin." Lovable concordou, e como não tem conceito de limites de segurança do lado do servidor vs. do cliente, colocou a chave onde era conveniente.

Mesmo quando apenas a chave anon é exposta (como pretendido), o modelo de segurança se desintegra se RLS não estiver configurado corretamente. O que nos traz ao grande.

Row Level Security: O Assassino Silencioso

Row Level Security (RLS) é o mecanismo de segurança primário do Supabase para proteger dados no nível do banco de dados. Quando configurado corretamente, garante que usuários só possam acessar seus próprios dados, independentemente de quais chamadas de API são feitas do cliente.

Quando auditamos os seis projetos Lovable, aqui está o que encontramos:

Projeto Total de Tabelas Tabelas com RLS Habilitado Tabelas com Políticas RLS Corretas Dados Sensíveis Expostos
SaaS Dashboard 14 6 3 PII do usuário, dados de cobrança
E-commerce App 22 10 4 Histórico de pedidos, endereços
Health Tracker 11 4 2 Registros de saúde, medicamentos
Project Manager 18 8 5 Dados do cliente, documentos
Booking Platform 16 7 3 Informações de contato, agendas
CRM Tool 20 9 4 Dados de clientes, notas

Leia novamente. No aplicativo de rastreamento de saúde -- que armazenava informações reais de medicamentos e saúde -- apenas 2 de 11 tabelas tinham políticas RLS corretas. Alguém com a chave anon (que é pública, lembre-se) poderia consultar os registros de saúde de todos os usuários.

As falhas mais comuns de RLS que vemos:

Políticas Ausentes Completamente

Lovable frequentemente cria tabelas sem habilitar RLS. No Supabase, RLS é desabilitado por padrão em novas tabelas, o que significa que qualquer pessoa com a chave anon pode ler todos os dados.

-- O que frequentemente encontramos: RLS nem mesmo habilitado
CREATE TABLE public.user_profiles (
  id UUID REFERENCES auth.users,
  full_name TEXT,
  email TEXT,
  phone TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Nenhum ALTER TABLE ... ENABLE ROW LEVEL SECURITY;
-- Nenhuma política definida

Políticas Excessivamente Permissivas

Quando Lovable adiciona RLS, as políticas são frequentemente muito amplas:

-- Política comum gerada por Lovable
CREATE POLICY "Users can view all profiles"
  ON public.user_profiles
  FOR SELECT
  USING (true);  -- Isto permite que QUALQUER usuário autenticado leia TODOS os perfis

A correção deveria ser:

-- Como deveria ser
CREATE POLICY "Users can view own profile"
  ON public.user_profiles
  FOR SELECT
  USING (auth.uid() = id);

Políticas DELETE e UPDATE Ausentes

Mesmo quando as políticas SELECT estão corretas, políticas INSERT/UPDATE/DELETE frequentemente estão ausentes ou incorretas. Encontramos casos onde qualquer usuário autenticado poderia atualizar o perfil de qualquer outro usuário.

Lovable Codebase Audit: Security Issues, RLS Gaps, and Exposed API Keys - architecture

Lacunas em Autenticação e Autorização

Lovable lida com autenticação básica razoavelmente bem -- configura Supabase Auth com email/senha ou logins sociais, e os fluxos de login/cadastro geralmente funcionam. Mas autenticação (quem você é?) e autorização (o que você pode fazer?) são coisas diferentes.

A camada de autorização é quase sempre incompleta ou inexistente.

Considere um aplicativo SaaS multi-tenant. Usuários pertencem a organizações. Eles devem ver apenas dados de sua organização. Eles podem ter diferentes papéis (admin, membro, visualizador). Lovable não gera nada disto.

O que normalmente encontramos:

// Busca de dados gerada por Lovable
const { data: projects } = await supabase
  .from('projects')
  .select('*')
  // Sem filtro para organization_id
  // Sem verificação do papel ou permissões do usuário

A correção requer mudanças no nível do banco de dados (RLS) e no nível da aplicação. Você precisa de uma tabela memberships mapeando usuários para organizações com papéis, políticas RLS que verificam a associação, e código de aplicação que filtra apropriadamente.

Este é o tipo de trabalho de arquitetura que é difícil parafusar depois de pronto. Toca cada query, cada componente, cada página. Se você está construindo um SaaS multi-tenant com Lovable, isto é a coisa que vai morder você com mais força.

Exposição de Lógica de Negócio do Lado do Cliente

Como Lovable gera aplicações React puras do lado do cliente, toda a lógica de negócio vive no navegador. Isto significa:

  • Cálculos de preços são visíveis e manipuláveis no DevTools do navegador
  • Sinalizadores de recurso para diferentes tiers de assinatura são verificados do lado do cliente
  • Códigos de desconto e lógica de validação estão no bundle JavaScript
  • Limitação de taxa de API não existe (não há servidor para executá-la)

Encontramos um SaaS gerado por Lovable onde a verificação de tier de preços era inteiramente do lado do cliente:

// Encontrado em um componente de projeto Lovable
const canAccessFeature = (feature: string) => {
  const plan = user?.subscription?.plan
  if (plan === 'pro') return true
  if (plan === 'basic' && BASIC_FEATURES.includes(feature)) return true
  return false
}

Um usuário poderia simplesmente modificar esta função no console do navegador -- ou chamar diretamente a API Supabase sem esta verificação -- e acessar recursos pro em um plano basic. O banco de dados não tinha políticas executando acesso baseado em plano.

Este é um problema arquitetural fundamental. A lógica de negócio precisa de um componente do lado do servidor. Seja API routes Next.js, Supabase Edge Functions, ou um serviço backend separado -- algo precisa validar operações onde o usuário não pode mexer.

Este é exatamente o motivo pelo qual frequentemente recomendamos frameworks como Next.js ou Astro para aplicações SaaS em produção -- eles fornecem renderização do lado do servidor e rotas de API pronto para usar.

Supabase Edge Functions: O que Está Faltando

Alguns projetos Lovable usam Supabase Edge Functions para certas operações -- tipicamente manipulação de webhooks Stripe ou envio de emails. Mas a implementação frequentemente tem problemas:

  1. Sem validação de entrada: Edge Functions aceitam e processam qualquer JSON enviado para elas sem validar forma, tipos ou restrições.

  2. CORS configurado para permitir todas as origens:

// Padrão comum em Edge Functions Lovable
const corsHeaders = {
  'Access-Control-Allow-Origin': '*',  // Permite que qualquer site chame isto
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
  1. Sem verificação de autenticação: A função não verifica o token JWT, então usuários não autenticados podem chamá-la.

  2. Assinatura de webhook Stripe não verificada: Em dois projetos, o manipulador de webhook Stripe não verificava a assinatura do webhook, o que significa que qualquer pessoa poderia enviar eventos de pagamento falsificados para o endpoint.

// O que encontramos -- sem verificação de assinatura
Deno.serve(async (req) => {
  const body = await req.json()
  // Processa diretamente o evento sem verificar se veio do Stripe
  if (body.type === 'checkout.session.completed') {
    // Atualiza a assinatura do usuário
    await supabaseAdmin.from('subscriptions').update({
      status: 'active',
      plan: 'pro'
    }).eq('user_id', body.data.object.metadata.user_id)
  }
})

Isto significa que um atacante poderia enviar uma requisição POST para a URL do webhook com um evento checkout.session.completed falsificado e atualizar qualquer usuário para um plano pro gratuitamente.

Padrões de Vulnerabilidade Comuns que Encontramos

Aqui está um resumo das questões mais comuns classificadas por severidade e frequência:

Vulnerabilidade Severidade Frequência (de 6) Exploibilidade
RLS ausente em tabelas sensíveis Crítico 6/6 Fácil -- apenas consulte a tabela
Políticas RLS excessivamente permissivas Alto 6/6 Fácil com chave anon
Exposição da chave service role Crítico 3/6 Trivial se encontrada
Sem verificação de webhook Stripe Alto 4/6 Médio -- precisa da URL do endpoint
Autorização apenas do lado do cliente Alto 6/6 Fácil com DevTools
Sem validação de entrada em Edge Functions Médio 5/6 Médio
CORS wildcard em Edge Functions Médio 5/6 Fácil
Dados sensíveis em localStorage Médio 4/6 Acesso físico ou XSS
Sem limitação de taxa Médio 6/6 Trivial
Referências diretas a objetos inseguras Alto 5/6 Fácil -- mude ID na URL

Como Auditar Seu Próprio Projeto Lovable

Se você construiu algo com Lovable que está manipulando dados de usuários reais, aqui está como verificar esses problemas você mesmo.

Passo 1: Verifique o Status RLS do Supabase

Vá para seu painel Supabase → Table Editor. Clique em cada tabela e verifique se RLS está habilitado. Depois vá para Authentication → Policies e revise cada política.

Ou execute esta query no SQL Editor:

SELECT
  schemaname,
  tablename,
  rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY tablename;

Se rowsecurity é false para qualquer tabela contendo dados de usuários, isso é um problema crítico.

Passo 2: Procure por Chaves Expostas

Em sua base de código, procure por:

grep -r "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" src/
grep -r "service_role" src/
grep -r "SUPABASE_SERVICE" src/

O primeiro padrão corresponde ao início das chaves JWT do Supabase. Se você encontrar a chave service_role em qualquer lugar em seu diretório src/, essa é uma vulnerabilidade crítica imediata.

Passo 3: Teste Políticas RLS

Crie uma segunda conta de usuário de teste. Faça login como aquele usuário e tente acessar dados do usuário um. Verifique a aba Network do navegador -- você está recebendo dados que não deveria?

Você também pode testar diretamente com curl:

curl 'https://YOUR_PROJECT.supabase.co/rest/v1/user_profiles?select=*' \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY"

Se isto retorna todos os perfis de usuários sem autenticação, RLS está quebrado.

Passo 4: Verifique Edge Functions

Revise cada Edge Function para:

  • Verificação de JWT
  • Validação de entrada
  • Configuração CORS
  • Verificação de assinatura de webhook (para manipuladores Stripe/pagamentos)

Passo 5: Inspecione o Bundle do Lado do Cliente

Execute npm run build e inspecione a saída. Procure no JavaScript construído por chaves de API, lógica de negócio e dados de preços. Qualquer coisa no bundle é visível para usuários.

Quando Reconstruir vs. Quando Remediar

Esta é a pergunta que cada fundador Lovable enfrenta uma vez que realiza que esses problemas existem. A resposta depende de vários fatores:

Remedie se:

  • Seu aplicativo tem menos de 10 tabelas
  • Você não tem um modelo de autorização complexo (sem multi-tenancy, sem papéis)
  • A arquitetura principal (modelo de dados, estrutura de página) é sólida
  • Você principalmente precisa adicionar políticas RLS e mover lógica do lado do servidor

Reconstrua se:

  • Você precisa de arquitetura multi-tenant com papéis e permissões
  • Sua lógica de negócio é complexa e tudo do lado do cliente
  • Você precisa de renderização do lado do servidor para SEO ou performance
  • O schema Supabase tem problemas estruturais significativos
  • Você está em uma escala onde precisa de infraestrutura adequada

Para remediação, você está tipicamente olhando para adicionar políticas RLS em todas as tabelas, migrar lógica sensível para Edge Functions ou uma camada do lado do servidor, implementar validação de entrada adequada, e adicionar limitação de taxa. Este é um projeto de 2-4 semanas para um desenvolvedor experiente dependendo da complexidade.

Para uma reconstrução completa, geralmente recomendamos uma arquitetura headless com separação adequada de preocupações. Custa mais upfront, mas dá a você uma base que escala. Verifique nossa página de preços para uma noção do que parece.

Se você não tem certeza qual caminho é certo, estamos felizes em fazer uma avaliação rápida. Nos contacte em nossa página de contato.

FAQ

É seguro usar Lovable para aplicações de produção? Lovable pode gerar um ponto de partida sólido, mas a saída precisa de endurecimento significativo de segurança antes de estar pronta para produção. O código gerado carece de políticas RLS apropriadas, validação do lado do servidor, e lógica de autorização. Pense nisto como andaimes, não o edifício acabado. Você absolutamente precisa de um desenvolvedor para revisar e assegurar o código antes que usuários reais confiem nele com seus dados.

O Lovable expõe minhas chaves de API Supabase? A chave Supabase anon é intencionalmente pública -- isso é por design, e o modelo de segurança Supabase conta com isso através de RLS. O problema é quando Lovable (ou você, através de prompts) coloca a chave service_role em código do lado do cliente. A chave anon sendo pública é apenas segura se suas políticas RLS forem à prova de balas, o que em projetos gerados por Lovable, tipicamente não são.

O que é Row Level Security e por que isso importa? Row Level Security (RLS) é um recurso PostgreSQL que Supabase usa para controlar quais linhas um usuário pode ler, inserir, atualizar ou deletar. Sem RLS, qualquer pessoa com sua chave anon pública pode consultar todo seu banco de dados -- dados de todos os usuários, todo registro privado. É o mecanismo de segurança único mais importante em uma aplicação com suporte Supabase, e é a coisa única mais frequentemente mal configurada em projetos Lovable.

Posso corrigir problemas de segurança Lovable por conta própria sem um desenvolvedor? Se você entende SQL e a sintaxe de política RLS do Supabase, você pode adicionar políticas RLS básicas você mesmo usando o painel Supabase. Porém, acertar as políticas -- especialmente para cenários complexos como multi-tenancy, recursos compartilhados, ou acesso de admin -- requer experiência. Uma política errada pode bloquear usuários dos seus próprios dados ou deixar tudo exposto. Para qualquer coisa além de um projeto pessoal simples, obtenha olhos profissionais nisto.

Como verifico se o banco de dados do meu aplicativo Lovable é seguro? O teste mais rápido: abra DevTools do seu navegador, vá para a aba Network, e procure pelas chamadas de API Supabase que seu app faz. Copie o valor do header apikey. Depois use curl ou Postman para consultar suas tabelas diretamente usando apenas aquela chave sem token de auth. Se você obtém dados de outros usuários -- ou qualquer dado em tabelas que deveriam ser privadas -- seu RLS está quebrado.

Quais são os maiores riscos de segurança com código gerado por IA em geral? Geradores de código IA otimizam para fazer as coisas funcionarem, não para fazer as coisas serem seguras. Eles não têm um modelo mental de sua paisagem de ameaças. Os maiores riscos são: segredos expostos, validação de entrada ausente, controles de acesso excessivamente permissivos, e a ausência de limites de segurança do lado do servidor. Esses não são únicos para Lovable -- problemas similares existem em código de Cursor, v0, Bolt, e outras ferramentas de IA. A diferença é que Lovable gera aplicações completas que as pessoas implantam diretamente em produção.

Devo mudar do Supabase para um backend diferente depois de usar Lovable? O Supabase em si é fino. É uma plataforma sólida com capacidades de segurança apropriadas. O problema é como Lovable a configura. Você não precisa abandonar Supabase -- você precisa configurar apropriadamente políticas RLS, mover operações sensíveis para Edge Functions, e adicionar a camada de autorização que Lovable pulou. A infraestrutura é boa; a configuração apenas precisa de trabalho.

Quanto custa corrigir problemas de segurança em um aplicativo gerado por Lovable? Para uma remediação direta -- adicionar políticas RLS, assegurar Edge Functions, remover chaves expostas, adicionar validação de entrada básica -- você está olhando para aproximadamente $3,000-$8,000 dependendo do número de tabelas e complexidade de seu modelo de autorização. Uma reconstrução completa com arquitetura apropriada corre $15,000-$50,000+ dependendo do escopo. O caminho de remediação é quase sempre mais custo-efetivo se seu modelo de dados principal é sólido.