Pular para o conteúdo

Noções de segurança

Esconder uma chave de API: mova para uma Edge Function do Supabase

Esconder uma chave de API é tirá-la do navegador, e uma Edge Function do Supabase é o menor lugar para guardá-la. Dois passos em volta pesam mais.

Vlad Tkachenko12 min de leitura
Uma folha com um furo em formato de chave, e atrás dela uma caixa lacrada com cadeado fechado e a chave visível por uma janelinha.

Em resumo

  • Nada no navegador sabe guardar segredo, então esconder uma chave de API é movê-la para algo que saiba. Uma Edge Function do Supabase é o menor servidor que você consegue ter.
  • A mudança são quatro passos. Troque antes a chave que você já publicou, porque a cópia no seu bundle antigo continua legível depois que você a tira de lá.
  • Uma function publicada exige um token por padrão, e a chave pública do seu próprio frontend atende essa exigência. Conferir quem está chamando é um passo à parte, e é ele que impede um desconhecido de gastar a sua cota.

Todo guia sobre uma chave de API vazada termina no mesmo lugar: mova para um servidor. Aí ele para. Você fica com um app que construiu no Lovable, no Bolt ou no Cursor, nenhum servidor à vista, e uma frase que presume que você já sabe o que fazer em seguida.

Esta é a parte que guia após guia deixa de fora. Mover a chave para uma Edge Function do Supabase tira ela da sua página, e sozinho isso não impede um desconhecido de usar. A própria documentação do Supabase diz por quê: a checagem que uma function publicada faz por padrão também aceita sua chave pública, e sua chave pública está no seu frontend, ao alcance de quem quiser copiar. Esconder a chave é a metade fácil. Sobre a outra metade ninguém escreve: decidir para quem a sua nova function responde.

Pense na chave como a chave-mestra de um estoque. Agora ela está colada por dentro da vitrine, onde qualquer um que parar consegue ler. Uma Edge Function é um quartinho dos fundos com um guichê: a chave vai para lá, e os visitantes pedem no guichê em vez de entrar e se servir. Se isso é uma melhora depende de para quem o guichê abre.

Onde coloco minha chave de API, se não é no frontend?

Em qualquer lugar que não seja o navegador. Uma Edge Function é a versão menor disso.

Um navegador não sabe guardar segredo. Tudo que sua página precisa para funcionar é baixado por cada visitante, e um visitante consegue ler tudo: o código, as imagens, os valores compilados dentro do código. Isso não é um defeito da sua ferramenta. É o que uma página web é. Quais chaves de API são seguras no frontend é a versão longa; a curta é que uma chave sk_, uma chave da OpenAI ou uma chave secreta do Supabase nunca ia sobreviver a uma viagem até o navegador.

Uma Edge Function do Supabase é um pedacinho de código que roda nas máquinas do Supabase. Ela consegue ler os segredos que você definiu no projeto, responde num endereço web próprio, e seu app chama ela pelo nome. Você escreve um arquivo, o Supabase executa, e não há servidor nenhum para alugar, atualizar ou manter acordado.

Em cima: a chave está no arquivo que cada visitante baixa, então cada visitante tem ela. Embaixo: o navegador pergunta para a function, e a chave nunca sai do Supabase.

Troque a chave que você já publicou, antes de mover qualquer coisa

A chave que está hoje no seu frontend já é pública, e continua pública depois que você tira ela do código.

Cada visitante que carregou seu site baixou ela. Cada robô também, e existem robôs automáticos percorrendo a web inteira atrás exatamente dessas sequências. Seu bundle antigo ainda está, além disso, no seu histórico de versões e em qualquer cache com uma cópia da sua página. Apagar uma linha de um arquivo que é seu não muda nada sobre um valor que já foi distribuído.

Então a ordem é: criar uma chave nova no fornecedor, segurar ela um instante, e revogar a antiga assim que a function abaixo estiver no ar. Depois leia sua página de cobrança e seus registros de uso de todo o período em que a chave antiga esteve solta. Trocar barra o que vem a seguir; sobre o que já aconteceu não tem efeito nenhum. Se a chave era uma chave secreta do Supabase, a ordem em que trabalhar tem um detalhe que vale ler antes.

Os quatro passos

Guardar o segredo, escrever a function, publicar, e mudar seu app para chamar a function em vez do fornecedor.

PassoNa linha de comandoNo painel
1. Guardar o segredosupabase secrets set MY_API_KEY=…Edge Functions → Secrets, depois Key e Value
2. Escrever a functionsupabase functions new forward-requestEdge Functions → Deploy a new function → Via Editor
3. Publicarsupabase functions deployO botão Deploy embaixo do editor
4. Chamar do seu appsupabase.functions.invoke('…')Igual, no código do seu app

Dentro da function o segredo chega como variável de ambiente, ou seja, um valor com nome que o código consegue ler e ninguém de fora. A documentação do Supabase sobre segredos de Edge Functions tem a referência completa, e três detalhes de lá poupam uma hora de confusão:

  • O nome de um segredo não pode começar com SUPABASE_. Esse prefixo é reservado para os valores que o Supabase define por você, e tanto o painel quanto a API recusam.
  • Um segredo novo já pode ser lido na hora. Você não precisa publicar a function de novo depois de trocar um.
  • Local e produção são separados. A pilha local lê supabase/functions/.env, que é um lugar diferente dos segredos do seu projeto no ar, então defina o valor nos dois, ou a function funciona na sua máquina e falha depois de publicada.

Depois apague a variável antiga do seu frontend. Se ela se chamava VITE_, NEXT_PUBLIC_ ou EXPO_PUBLIC_, esse prefixo era a instrução de compilar o valor dentro do bundle, e o prefixo é a história inteira de como ele foi parar lá.

Exigir um token quer dizer que só meus usuários conseguem chamar?

Não, e este é o ponto deste artigo que vale ler duas vezes.

O Supabase liga por padrão uma checagem chamada verify_jwt. Ela olha o cabeçalho Authorization antes do seu código rodar e recusa a requisição se não houver nada válido ali. Parece uma porta que só seus usuários abrem. Não é, porque o Supabase documenta também que a mesma checagem aceita uma chave pública ou secreta em qualquer um dos dois cabeçalhos, por compatibilidade com projetos mais antigos, e acrescenta com todas as letras que a checagem sozinha não autentica quem chama mandando só uma chave de API.

Sua chave pública está no seu frontend. Isso está certo e é para ser assim. Também quer dizer que quem abrir seu site, ler a chave e mandar ela para a sua function nova passa pela checagem da plataforma e entra no seu código.

No estoque, verify_jwt é um porteiro que confere se você está com um crachá de visitante. Todo visitante tem um, porque é você quem distribui na entrada. Quem está no guichê faz outro serviço, e é essa pessoa que pergunta qual visitante você é.

Os dois passam pela checagem da plataforma. Só o segundo passa por uma checagem de quem está chamando, e essa segunda é a que você precisa acrescentar.

Trancar a function, para não ter construído um proxy aberto

Decida quais chamadores sua function aceita, e escreva essa decisão dentro da function.

O Supabase entrega um invólucro para isso, então é uma linha e não um projeto. Coloque auth: 'user' e a function aceita o token de um usuário logado e entrega ao seu código um cliente de banco já limitado às regras de Row Level Security desse usuário:

import { withSupabase } from 'npm:@supabase/server@1'

export default {
  fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
    // ctx.userClaims diz quem está chamando. Recuse aqui o que você não quer,
    // e então chame o fornecedor com o segredo vindo do ambiente.
    return Response.json({ ok: true })
  }),
}

A página Securing Edge Functions lista os outros modos, e dois deles importam num app comum. Uma function chamada por outra máquina e não por um navegador usa auth: 'secret', com a chave secreta mandada no cabeçalho apikey. Uma function que recebe webhooks da Stripe ou do GitHub não pode usar nenhum dos dois, porque esses fornecedores não têm token nenhum seu; ela coloca verify_jwt = false e confere a assinatura do fornecedor dentro do handler.

Seja qual for a escolha, decida com cuidado o que acontece quando aparece um chamador que você não esperava. O exemplo que o Supabase dá de uma function que pode aceitar qualquer um é uma verificação de saúde, e uma verificação de saúde não custa nada quando um desconhecido chama. Uma function que repassa uma chamada de API paga cobra de você cada uma delas.

Posso usar service_role numa Edge Function?

Pode. Uma function é o único lugar em que uma chave secreta do Supabase cabe.

E você nem precisa colar ela. O Supabase coloca as chaves do projeto no ambiente da function, então o código lê de lá e não de um segredo definido por você. Projetos novos recebem SUPABASE_SECRET_KEYS e SUPABASE_PUBLISHABLE_KEYS, cada um um pequeno dicionário de chaves com nome; projetos mais antigos recebem SUPABASE_SERVICE_ROLE_KEY e SUPABASE_ANON_KEY com os nomes de sempre. O que mudou quando o Supabase renomeou as chaves diz qual par você tem.

Use a chave secreta para o trabalho que realmente precisa ver cada linha, como gravar um registro de auditoria que o usuário não possa editar. Para tudo que um usuário lê sobre si mesmo, use o token dele e deixe suas regras de Row Level Security filtrarem, que é para isso que elas existem.

Conferir pelo navegador, a única prova que vale

Carregue seu site no ar, abra a aba de rede, e veja o que seu navegador manda de verdade.

As duas coisas para conferir:

  • Nenhuma requisição leva a chave. Passe pela parte do seu app que antes chamava o fornecedor. A requisição de saída tem que ir para …supabase.co/functions/v1/sua-function, e a única credencial lá dentro tem que ser sua chave pública ou o token do seu usuário.
  • A chave não está no código baixado. Use a busca do navegador em todos os arquivos carregados e cole a primeira dúzia de caracteres da chave antiga. Nenhum resultado é a resposta que você quer.

Só uma nova compilação e uma nova publicação tiram um valor do bundle. Uma chave que ainda aparece na busca costuma querer dizer que o frontend saiu antes de a variável sair dele.

Nosso scan gratuito faz a segunda conferência de fora e diz o que consegue ler no seu bundle no ar, inclusive qual chave do Supabase você publicou. Leva uns 20 segundos e não precisa de conta: escaneie seu app.

Como fazer isso a partir do Lovable, Bolt ou Replit

Use o painel do Supabase. Não tem nenhum passo de terminal ali.

Abra seu projeto, escolha Edge Functions na barra lateral e depois Deploy a new function → Via Editor. O guia rápido do painel do Supabase percorre tudo com capturas de tela, e entre os modelos oferecidos há um para repassar a um fornecedor de IA, que é exatamente o formato deste trabalho. A publicação leva entre dez e trinta segundos, e a function fica no ar num endereço próprio. Seu segredo vai na página Edge Function Secrets, na mesma seção.

Uma ressalva que vale conhecer antes de confiar nisso: o Supabase escreve que o editor do painel não tem controle de versão, nem versões, nem volta atrás, e recomenda ele para trabalho rápido e não para código que vai ficar. Apertar Deploy sobrescreve o que estava lá. Se a function acabar fazendo algo que importa para você, baixe ela por essa página e guarde o arquivo.

Existe também uma versão desta pergunta no formato Replit, porque o Replit tem um armazém de segredos próprio e seu app roda um servidor próprio lá. O que os Replit Secrets fazem de verdade é essa versão.

Quando uma Edge Function é a resposta errada

Dois casos, e nos dois a mudança custa trabalho e não traz nada.

A chave era pública de propósito. Uma chave pk_live_ da Stripe, uma chave pública do Supabase ou uma chave anon, um token público do Mapbox: essas são feitas para ficar num navegador, e a proteção está em outro lugar. Colocar uma delas atrás de uma function acrescenta um desvio e não tira risco nenhum. Quais chaves são essas é a lista.

A chave pode ser restrita ao seu próprio site. As chaves de navegador do Google são o exemplo mais comum: no console do Google você limita uma delas aos seus domínios, e é justamente essa a solução que o Google prevê para esse caso. A chave continua legível e deixa de servir para quem a copiar.

Todo o resto fica num servidor. Uma chave da OpenAI ou da Anthropic não tem variante pública, e é por isso que uma chave de fornecedor de IA no seu frontend não tem ajuste nenhum que a salve, e nenhuma minificação esconde uma chave secreta da Stripe de quem ler sua página.

O que fazer agora

O que fazer

  • Troque a chave primeiro. Crie uma nova no fornecedor, coloque no segredo da function, e revogue a antiga assim que a function estiver no ar.
  • Guarde o segredo no seu projeto do Supabase, não no seu repositório e não numa variável VITE_. Defina separadamente para desenvolvimento local e para produção.
  • Publique uma function que usa o segredo, e mude seu app para chamar a function pelo nome em vez do fornecedor.
  • Acrescente uma checagem de quem está chamando. verify_jwt sozinho aceita a chave pública do seu próprio frontend, então não barra um desconhecido.
  • Apague a variável antiga, compile de novo, e confirme na aba de rede do navegador que nada que sai leva a chave.
  • Leia cobrança e uso de todo o período em que a chave antiga ficou pública. Trocar barra a próxima cobrança e não a última.

Se você prefere revisar o app inteiro em vez de uma chave só, a checklist de segurança em 10 minutos cobre isso junto com as outras coisas que vale fechar num app recém-lançado.

Perguntas frequentes

Onde coloco minha chave de API, se não é no frontend?

Em qualquer lugar que não seja o navegador. Uma Edge Function do Supabase é a opção menor: você guarda a chave como segredo no seu projeto, escreve um arquivo pequeno que usa ela, e seu app chama essa function pelo nome em vez de chamar o fornecedor direto. Uma rota serverless na sua hospedagem ou um servidor seu funcionam igual. O que elas têm em comum é que a chave é lida onde seu visitante não vê.

O que é uma Edge Function do Supabase?

Um pedacinho de código que roda nas máquinas do Supabase e não no navegador do seu visitante. Ela responde num endereço web próprio, consegue ler os segredos que você definiu no projeto, e seu app chama ela com supabase.functions.invoke. Você escreve um arquivo, o Supabase executa, e não há servidor nenhum para alugar ou manter.

Preciso trocar a chave depois de mover?

Precisa, e antes de tudo. A chave que estava no seu frontend foi baixada por cada visitante e por cada robô que leu seu site, e tirar ela do código não muda nada sobre essas cópias. Crie uma chave nova no fornecedor, coloque no segredo da sua Edge Function e revogue a antiga. Depois confira cobrança e uso em todo o período em que a chave antiga esteve valendo.

Qualquer pessoa pode chamar minha Edge Function?

Por padrão uma function recusa uma requisição sem token nenhum, mas essa checagem é mais fraca do que parece. O Supabase documenta que a checagem da plataforma aceita também sua chave pública ou secreta, e sua chave pública está no seu frontend, onde qualquer um consegue ler. Então a checagem barra uma requisição vazia e não uma decidida. Para aceitar só seus usuários logados, verifique quem chamou dentro da function.

Posso usar service_role numa Edge Function?

Pode. Uma Edge Function é o único lugar em que uma chave secreta do Supabase cabe, e o Supabase coloca as chaves do projeto no ambiente da function para você, então você nunca cola nenhuma. Use para o trabalho que precisa ver cada linha, e use o token de quem chamou para tudo que deve respeitar suas regras de Row Level Security.

Dá para fazer isso sem terminal?

Dá. O painel do Supabase tem uma seção Edge Functions onde você escreve uma function no navegador, aperta Deploy e define seu segredo na página Edge Function Secrets. O Supabase avisa que o editor do painel não guarda histórico de versões, então recomenda ele para trabalho rápido e deixa a linha de comando para o que você pretende manter.

Escrito por

Vlad Tkachenko

Fundador da Reeve

Passo meus dias olhando apps criados com Lovable, Bolt, v0, Cursor e Replit, e a curta lista de erros que aparecem neles sem parar.

Mais sobre o autor

Leia depois

Todos os artigos

Não sabe como está o seu próprio app?

Faça um scan gratuito e receba uma nota clara de A a F em cerca de 20 segundos. Sem conta e sem cartão.

Verificar meu app grátis

Verificação externa automatizada, não uma auditoria completa. A ausência de achados não é garantia de segurança.