← Todas as verificações
SegurançaSeveridade típica: Crítico

Scanner de segredos em JavaScript

Procura chaves de API e credenciais (Stripe, AWS, Supabase) reveladas no JavaScript do navegador.

O que verifica

Este scanner revê o JavaScript que o teu site envia ao navegador à procura de padrões de chaves de API e credenciais, como as da Stripe, AWS ou Supabase. Deteta segredos que foram incluídos por engano no código do cliente. Só analisa o que já é público para qualquer visitante.

Porque importa

Tudo o que vai no JavaScript do navegador é visível para qualquer pessoa que abra as ferramentas de programador. Uma chave secreta aí pode ser usada para aceder aos teus serviços, gastar o teu dinheiro ou ler os dados dos teus utilizadores. É um erro muito comum quando se misturam variáveis de servidor e de cliente.

Como se corrige

Move todas as chaves secretas para o servidor e acede a elas apenas a partir do backend, nunca do código do navegador. Usa só chaves públicas ou restritas no cliente e roda qualquer segredo que já tenha sido exposto. Em muitas frameworks, as variáveis sem o prefixo público ficam apenas no servidor.

Como funciona esta verificação

O Scanaris carrega a tua página principal, recolhe os ficheiros JavaScript que ela liga a partir do teu próprio domínio e lê-os à procura de padrões de credenciais conhecidas: chaves secretas e restritas do Stripe (sk_live_, rk_live_), identificadores de acesso da AWS (AKIA…), chaves de API da Google (AIza…), chaves da OpenAI, tokens do GitHub (ghp_…) e chaves privadas em formato PEM. Além disso descodifica qualquer JWT que encontre e, se a carga tiver role: service_role, marca-o como chave de serviço do Supabase exposta, o que é crítico. As chaves públicas (pk_live_ do Stripe, a anon ou publishable do Supabase, a configuração do Firebase) são excluídas de propósito: foram desenhadas para ir no navegador.

O que um atacante faz com isto

Exatamente o mesmo que o scanner, mas em escala: há robôs que descarregam o JavaScript de domínios novos assim que aparecem nos registos de certificados e testam cada chave que encontram. Uma chave secreta do Stripe permite criar reembolsos ou ler clientes; uma da OpenAI, gastar a tua quota; uma service_role do Supabase, ler e apagar toda a tua base de dados saltando as políticas. Não é preciso "hackear" nada: o ficheiro é público por definição, cada visitante recebe-o.

Como é que uma chave acaba no bundle

Quase sempre por uma variável de ambiente com o prefixo público: NEXT_PUBLIC_ em Next.js, VITE_ em Vite, NUXT_PUBLIC_ em Nuxt. A framework embute essas variáveis no JavaScript no momento do build, que é precisamente o que queres para uma chave pública e precisamente o que não queres para uma secreta. O outro caminho habitual é colar a chave no chat do Lovable, do Bolt ou do Cursor para "ligar" um serviço: o modelo escreve-a no componente que precisa dela. Mais detalhe em segredos que a tua app deixa fugir no navegador.

Como corrigir

  • Roda a chave primeiro. Dá-a por comprometida mesmo que a tires do código: os bundles anteriores continuam em caches e no histórico de deploys. Gera uma nova no painel do serviço e revoga a antiga.
  • Move a chamada para o servidor. Em Next.js, para um Route Handler ou uma Server Action; em Nuxt, para uma rota de server/api; com Supabase, para uma Edge Function; na Netlify, para uma função. O navegador chama o teu servidor e o servidor, com a chave, chama o serviço.
  • Tira o prefixo público à variável e guarda-a no painel do teu alojamento como variável de servidor. Se uma variável precisa do prefixo, é porque é pública: pergunta-te se devia ser.
  • Revê o histórico do git. Se a chave alguma vez foi enviada para um repositório, está no histórico mesmo que o ficheiro atual esteja limpo.

Confirma que está limpo

curl -s https://oteudominio.com | grep -oE 'src="[^"]+\.js[^"]*"' | cut -d'"' -f2 | head -5
curl -s https://oteudominio.com/assets/index-XXXX.js | grep -oE "sk_live_[A-Za-z0-9]+|rk_live_[A-Za-z0-9]+|AKIA[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{35}|ghp_[A-Za-z0-9]{36}|BEGIN (RSA |EC )?PRIVATE KEY" | head

O primeiro comando lista os teus scripts; o segundo procura os padrões num deles. Se não sair nada em nenhum, o bundle está limpo. Repete depois de cada deploy grande, porque uma regeneração com IA pode voltar a meter a chave.

Perguntas frequentes

A chave anon do Supabase ou a pk_live do Stripe são um problema?

Não. São públicas por desenho e o scanner não as marca. O que protege os teus dados com a anon é o RLS; o que protege o Stripe com a pk_live é que só serve para criar tokens de pagamento, não para cobrar.

Marca-me uma chave da Google que uso para o Maps. É grave?

As chaves AIza podem ser legitimamente públicas se estiverem restringidas por domínio e por API na consola do Google Cloud. Sem restrições, qualquer pessoa pode gastar a tua quota. Restringe-a e o risco real desaparece, mesmo que o padrão continue a aparecer.

Já a tirei do código e continua a aparecer.

O bundle publicado pode ser o antigo, ou a chave pode estar noutro ficheiro (um chunk partilhado, um script de terceiros alojado no teu domínio). Faz deploy de novo, volta a analisar e, em qualquer caso, roda a chave.

Verifica-o no teu site

Analisa o teu site grátis e vê se esta e outras 44 verificações passam, com um fix prompt por cada problema.

Analisar o meu site grátis

Do blog

Verificações relacionadas