"Segurança no Bolt" são duas coisas que convém separar. A primeira é o projeto do Bolt em si: o que colas no chat, onde acabam as variáveis de ambiente, com quem partilhas o projeto e para onde é feito o deploy. A segunda é o código que o Bolt gera, quase sempre uma SPA com Vite e React ligada ao Supabase, que traz as mesmas cinco ou seis falhas de sempre. Este guia cobre as duas, com um ficheiro de instruções para que o Bolt aplique as regras em cada geração, um prompt de revisão antes do deploy e a forma de verificar o resultado de fora.
Parte 1: proteger o teu projeto do Bolt
O Bolt corre no teu navegador, e isso muda onde vivem as chaves
O Bolt executa o projeto inteiro dentro do navegador com WebContainers. Não há servidor próprio enquanto desenvolves, por isso qualquer chave que ponhas num ficheiro .env está no projeto, e se tiver o prefixo VITE_ acaba embutida no bundle que é publicado. Para o navegador isso só é correto com chaves públicas (a anon do Supabase, a publishable do Stripe). As secretas não têm lugar no frontend: vão para uma Edge Function do Supabase ou uma função da Netlify, e o cliente chama a função.
O que colas no chat acaba no código
Se colas uma chave na conversa para "ligar" um serviço, o modelo escreve-a onde precisa dela, que normalmente é um componente. A partir daí viaja para todos os visitantes. A regra: as chaves públicas vão no .env; as secretas, no painel do serviço que executa a função (Supabase ou Netlify), nunca no chat. E se colaste uma por engano, roda-a: o histórico conserva-a mesmo que a apagues do código.
Partilhar, exportar e fazer deploy
Um link de projeto partilhado mostra o código completo, incluindo o .env. Antes de partilhar, revê o que está lá dentro. Se exportas para o GitHub, que o repositório seja privado e que o .env esteja no .gitignore. O deploy com um clique vai para a Netlify, num subdomínio .netlify.app ou no teu domínio; a Netlify dá TLS e redirecionamento para HTTPS, mas não cabeçalhos de segurança, e as variáveis de ambiente de produção configuram-se no painel dela, não no .env do projeto.
O Supabase é teu
A integração do Supabase cria tabelas, políticas e funções a partir do chat. RLS, buckets e definições de Auth continuam a ser responsabilidade tua, e o Bolt comete os mesmos erros que o Lovable com as políticas. O guia de segurança para Supabase tem três consultas SQL para auditar o projeto num minuto.
Parte 2: as falhas que o Bolt mete no teu código
- Chaves secretas em componentes. Uma chamada à OpenAI ou ao Stripe a partir do React com a chave lá dentro. Procura
sk_,service_roleeBearerno teu bundle publicado. - Tabelas sem RLS ou com políticas
using (true). Quando uma consulta falha por permissões, o modelo abre a política em vez de a corrigir. - Edge Functions sem autenticação. O template responde a qualquer pessoa com
Access-Control-Allow-Origin: *; se a função gasta dinheiro, qualquer pessoa o pode gastar. - Verificações só na interface. A página de administração esconde-se se não fores administrador, mas a API responde na mesma.
- Source maps em produção. Alguns templates de Vite trazem
build.sourcemap: true; publica o teu código original ao lado do minificado. - Invisível para a IA e os motores de busca. O HTML chega vazio e os bots do ChatGPT ou do Perplexity não veem nada. Se a landing importa, pré-renderiza-a ou separa-a. Detalhe em porque é que as apps feitas com IA são invisíveis para a IA.
- Sem cabeçalhos de segurança. Nem CSP, nem HSTS, nem X-Frame-Options. Na Netlify acrescentam-se com um ficheiro.
Um bloco para o Bolt aplicar sempre
O Bolt lê o ficheiro .bolt/prompt do teu projeto como instruções permanentes. Cria o ficheiro e cola isto:
Regras de segurança deste projeto:
- Nunca ponhas chaves secretas no código do frontend nem em variáveis VITE_. As integrações com chaves secretas vão numa Edge Function do Supabase ou numa função da Netlify que lê a chave das suas variáveis de ambiente.
- Toda a tabela nova tem RLS ativado e uma política explícita por operação. Não uses using (true) nem with check (true) exceto em conteúdo público só de leitura, e avisa-me quando o fizeres.
- Toda a função de servidor verifica o JWT do utilizador, limita Access-Control-Allow-Origin ao nosso domínio e valida a entrada.
- As permissões de administrador verificam-se no RLS ou no servidor, nunca só na interface.
- Os ficheiros de utilizadores vão para buckets privados com políticas e URLs assinados.
- build.sourcemap fica desativado em produção.
- Põe título, meta descrição e etiquetas Open Graph reais no index.html.
E os cabeçalhos para a Netlify, num ficheiro public/_headers que é publicado com a app:
/*
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Um prompt de revisão antes do deploy
Revê a segurança deste projeto e não mudes nada ainda. Responde com uma lista:
1. Chaves ou tokens no código do frontend ou em variáveis VITE_, com ficheiro e linha.
2. Tabelas sem RLS e políticas com using (true) ou with check (true).
3. Funções de servidor que não verifiquem o JWT, que respondam com Access-Control-Allow-Origin: * ou que não validem a entrada.
4. Ações de administrador protegidas só na interface.
5. Buckets públicos com ficheiros de utilizadores.
6. build.sourcemap ativado e ausência do ficheiro _headers.
7. Metadados do index.html que continuem a ser os do template.
Para cada ponto, propõe a alteração exata e espera pela minha confirmação.
Verifica o resultado de fora
curl -sI https://oteudominio.com | grep -iE "strict-transport|x-frame|x-content-type"
curl -s https://oteudominio.com | grep -oE '/assets/[^"]+\.js' | head -1
curl -s https://oteudominio.com/assets/index-XXXX.js | grep -oE "sk_live_[A-Za-z0-9]+|service_role|eyJhbGciOi[A-Za-z0-9_-]{20,}" | head -3
curl -sI https://oteudominio.com/assets/index-XXXX.js.map | head -1
O esperado: cabeçalhos presentes, nenhuma chave no chunk e um 404 no source map. Depois testa a API do Supabase sem sessão como explica o guia do Supabase. O Scanaris faz estas verificações e cerca de quarenta mais em 30 segundos: deteta que a app é do Bolt, procura segredos no bundle, verifica source maps, cabeçalhos, CORS, cookies e ficheiros expostos, testa as tabelas do Supabase sem sessão e revê se o teu conteúdo é legível sem JavaScript. Por cada falha dá-te um fix prompt para colar no chat do Bolt, e pode voltar a analisar todos os dias, porque uma regeneração pode trazer de volta uma falha corrigida.
Perguntas frequentes
As variáveis VITE_ são privadas?
Não. O Vite embute no bundle tudo o que comece por VITE_; é a forma dele de dizer "isto é público". As chaves secretas não levam esse prefixo e não se leem a partir do frontend.
Posso pôr cabeçalhos de segurança na Netlify?
Sim, com o ficheiro _headers na pasta pública ou com o netlify.toml. A Netlify aplica-os no deploy seguinte; confirma com curl.
O que faço se colei uma chave secreta no chat do Bolt?
Roda-a no serviço de origem mesmo que a tenhas apagado do código: o histórico do projeto e os deploys anteriores conservam-na. Depois acrescenta-a no painel do Supabase ou da Netlify e pede ao Bolt que mova a integração para uma função.