Segurança
O Floopy fica entre sua aplicação e os provedores de LLM, processando chaves de API, prompts, respostas e dados de faturamento em escala. A segurança é incorporada em cada camada — desde o runtime Rust, que elimina classes inteiras de vulnerabilidades em tempo de compilação, até o sistema de criptografia em camadas que protege seus dados em repouso e em trânsito.
Princípios de Segurança
O modelo de segurança do Floopy é construído em torno de três princípios:
Defesa em profundidade. Cada requisição passa por múltiplas camadas de segurança independentes (rate limiting, autenticação, validação de assinatura, LLM firewall) antes de chegar a um provedor. Uma falha em uma camada não compromete as demais.
Zero trust no conteúdo. Prompts e respostas são tratados como entrada não confiável. O LLM firewall verifica ataques de injeção e conteúdo inseguro antes de encaminhar. Logs contendo conteúdo sensível passam por remoção automática de PII.
Falhar com segurança, não falhar aberto. Falhas em dependências externas (ClickHouse fora do ar, Redis inacessível, timeout do provedor) degradam a funcionalidade de forma controlada, sem expor dados ou burlar verificações de segurança. Falhas de autenticação rejeitam a requisição — nunca a deixam passar.
Separação Arquitetural
graph TB
subgraph Gateway["Olympus — AI Gateway (Rust)"]
RL[Rate Limiter] --> Auth[API Key Auth]
Auth --> Sub[Subscription Check]
Sub --> FW[LLM Firewall]
FW --> SSRF[SSRF Validation]
SSRF --> Router[Provider Router]
end
subgraph Dashboard["Zeus — Dashboard (Next.js)"]
SA[Supabase Auth] --> RBAC[Org Access Control]
RBAC --> Actions[Server Actions]
end
App[Your Application] --> Gateway
Gateway --> Providers[LLM Providers]
Gateway -.->|Async Logs| CH[(ClickHouse)]
Dashboard --> Supabase[(Supabase)]
Dashboard --> CHO gateway e o dashboard são arquiteturalmente separados. O Zeus se conecta diretamente ao Supabase e ao ClickHouse — nunca faz proxy pelo Olympus. Um gateway comprometido não consegue acessar dados do dashboard, e um dashboard comprometido não afeta o processamento de requisições do gateway.
Segurança de Chaves de API
Formato da Chave
As chaves de API do Floopy utilizam um formato estruturado com prefixo e checksum:
flo_sk_live_<32_random_chars>_<crc32_checksum>- Prefixo
flo_sk_— permite detecção automática por ferramentas de varredura de segredos (GitHub Advanced Security, GitGuardian, TruffleHog). Se sua chave vazar em um repositório público, essas ferramentas a identificam instantaneamente. - Modo
live/test— distingue chaves de produção de chaves de sandbox. - Checksum CRC-32 — permite validação no lado do cliente antes de fazer uma requisição de rede. Detecta erros de digitação e truncamento imediatamente.
Armazenamento de Chaves
As chaves de API nunca são armazenadas em texto puro. Quando você cria uma chave, ela é exibida uma única vez — depois é hasheada com SHA-256 antes do armazenamento. Toda consulta subsequente compara hashes, não valores em texto puro. Não há como recuperar a chave original a partir do banco de dados.
Cache de Autenticação
As consultas de chaves são armazenadas em cache no Redis com um TTL configurável para evitar idas ao banco de dados a cada requisição. Quando você revoga ou rotaciona uma chave no dashboard, o cache é invalidado imediatamente — a revogação entra em vigor em segundos, não em minutos.
Criptografia de Chaves de Provedor
Suas chaves de API de provedores de LLM (OpenAI, Anthropic, Gemini, etc.) são criptografadas em repouso usando criptografia de envelope XChaCha20-Poly1305:
- Cada chave de provedor recebe sua própria Data Encryption Key (DEK)
- A DEK é criptografada por uma Key Encryption Key (KEK) gerenciada via KMS
- Nonces aleatórios de 192 bits eliminam riscos de colisão por paradoxo do aniversário que afetam AES-GCM (nonces de 96 bits)
- As chaves são descriptografadas apenas em tempo de execução ao encaminhar para o provedor — nunca ficam em cache em texto puro
- O material da DEK em texto puro é apagado da memória (
zeroize) imediatamente após o uso
Rotação de KEK
A KEK usada para envelopar cada DEK pode ser rotacionada sem downtime. Múltiplas versões de KEK coexistem: novas chaves de provedor são criptografadas com a KEK atual, enquanto ciphertexts existentes continuam sendo descriptografados pela KEK que originalmente os envelopou. Cada registro criptografado carrega sua versão de KEK e o timestamp da criptografia para fins de auditoria, e o byte de versão é criptograficamente ligado ao ciphertext via AEAD — qualquer alteração falha na autenticação em vez de forçar a busca de outra chave.
Rate Limiting
O Floopy implementa rate limiting em camadas para prevenir abuso e ao mesmo tempo ser justo com clientes empresariais:
| Tier | Key | Default Limit | Purpose |
|---|---|---|---|
| Anônimo | Endereço IP | 20 rpm | Bloquear abuso não autenticado |
| Autenticado | ID da Organização | Baseado em plano (RPS / 10s / RPM) | Uso justo (não por IP — seguro atrás de NATs) |
| Por Chave | ID da Chave de API | Configurável | Controle granular por aplicação |
Tiers autenticados aplicam três janelas deslizantes simultâneas — 1 segundo (RPS), 10 segundos (burst) e 60 segundos (RPM) — avaliadas atomicamente em um único round-trip no Redis. Quando uma requisição excede uma janela, a resposta retorna 429 com o header Floopy-RateLimit-Exceeded: rps|w10|w60 identificando qual janela foi excedida. Padrões por plano: Free 5/50/60, Starter 20/200/300, Pro 100/1.000/10.000, Enterprise personalizado.
Os rate limits usam janelas deslizantes atômicas no Redis para consistência em escalabilidade horizontal. Requisições autenticadas são limitadas por organização, não por IP — isso evita limitação indevida para clientes empresariais atrás de NATs corporativos ou IPs de saída compartilhados.
LLM Firewall
Camada unica de seguranca de prompt executada antes de qualquer requisicao chegar a um provedor de LLM.
Um LLM ajustado para seguranca (configurado por FIREWALL_MODEL, padrao meta-llama/Llama-Guard-4-12B no Together — a mesma notacao aceita Bedrock ou distribuicoes ponderadas) classifica cada prompt como safe ou unsafe. Categorias consideradas inseguras: injecao de prompt, tentativas de jailbreak, autolesao, conteudo sexual envolvendo menores, instrucoes de atividades ilegais, discurso de odio.
Um cache de vereditos no Qdrant fica na frente da chamada ao LLM. Quando o embedding da requisicao bate com um veredito unsafe recente acima do FIREWALL_SEMANTIC_CACHE_THRESHOLD (padrao 0.95), o veredito em cache evita a chamada ao LLM. Apenas vereditos unsafe sao cacheados — safe sempre e recalculado para que uma troca de modelo possa inverter o resultado sem etapa manual.
- Ative por requisicao via header
floopy-llm-security-enabled: true - Bloqueado pelo plano via
has_advanced_firewall - Requisicoes bloqueadas retornam
400 PROMPT_THREAT_DETECTED - Cada chamada registra
backend+model_refna linha do request; cache hits gravammodel_ref="cache:firewall_verdicts" - Comportamento fail-open: qualquer falha do LLM (rede, parse) deixa a requisicao passar com log loud
Proteção contra SSRF
O gateway valida todas as requisições de saída contra uma lista de permissões estrita de provedores:
- Apenas hostnames de provedores conhecidos são permitidos (
api.openai.com,api.anthropic.com,generativelanguage.googleapis.com,api.groq.com,api.mistral.ai,api.deepseek.com) - Resultados de resolução DNS são verificados contra faixas de IP privadas/reservadas (RFC 1918, RFC 6598, link-local, loopback, CGNAT)
- URLs fornecidas pelo cliente nunca influenciam o destino — o provedor é determinado pelo mapeamento de modelo
- Tentativas de SSRF são registradas como eventos de segurança
Sanitização de Headers
Os headers do cliente são sanitizados antes de serem encaminhados aos provedores usando uma abordagem de blocklist:
- Removidos:
authorization,cookie,set-cookie,proxy-authorization,x-forwarded-for,x-forwarded-host,x-forwarded-proto,x-real-ip,host - Encaminhados: Todos os demais headers, incluindo os específicos de provedores (
anthropic-version,OpenAI-Organization) - A autenticação do provedor é injetada após a remoção — suas chaves de provedor nunca se misturam com headers do cliente
Remoção de PII
Os corpos de requisição e resposta passam por remoção automática de PII antes do registro no ClickHouse:
| Pattern | Example | Replacement |
|---|---|---|
| Endereços de email | user@example.com | [REDACTED:email] |
| Números de CPF | 123.456.789-00 | [REDACTED:cpf] |
| SSN | 123-45-6789 | [REDACTED:ssn] |
| Números de cartão de crédito | 4111 1111 1111 1111 | [REDACTED:credit_card] |
| Números de telefone | +55 11 91234-5678 | [REDACTED:phone] |
| Chaves de API | sk-abc123... | [REDACTED:api_key] |
| Tokens Bearer | Bearer eyJ... | [REDACTED:bearer] |
A remoção acontece no caminho assíncrono de logging — nunca bloqueia ou desacelera sua requisição.
Redação de Atributos de Trace
Além da remoção de PII dos corpos, os atributos de span (metadados anexados aos traces distribuídos) passam por uma camada dedicada de redação antes de serem enviados ao ClickHouse ou a qualquer coletor OTLP por organização. Expressões regulares detectam tokens Bearer ..., chaves de provedor no formato sk-... / sk-ant-... / AIza..., e valores opacos longos típicos de segredos; chaves de atributo sensíveis (authorization, api_key, token, secret, password) são redigidas por completo independentemente do valor. A redação acontece na camada do worker, então é aplicada de forma uniforme no sink ClickHouse e em todos os destinos OTLP externos.
Segurança do Dashboard
| Protection | Implementation |
|---|---|
| Autenticação | Supabase Auth com sessões baseadas em cookies (httpOnly, secure, sameSite: lax) |
| Acesso à organização | Toda server action valida a associação do usuário via requireOrgAccess() |
| Operações administrativas | Ações destrutivas exigem requireOrgAdmin() (papel de owner ou admin) |
| Headers de segurança | X-Frame-Options: DENY, X-Content-Type-Options: nosniff, HSTS (2 anos), Permissions-Policy (câmera/microfone/geolocalização desabilitados) |
| Content Security Policy | Restringe origens de scripts, frame ancestors e alvos de conexão |
| Segurança de queries | Todas as queries do ClickHouse usam sintaxe parametrizada {name:Type} — sem interpolação de strings |
| Rate limiting | 120 requisições/minuto por usuário em todas as server actions |
| Limite de tamanho do body | Limite de 2 MB nos payloads de server actions |
Controle de Acesso Baseado em Papéis
O Floopy utiliza um modelo RBAC de três camadas para que o acesso concedido em um escopo mais amplo (um grupo de organizações) flua para todos os recursos dentro daquele escopo, enquanto associações mais específicas permanecem autoritativas no seu próprio nível.
Os três escopos
| Escopo | Tabela no Supabase | O que controla |
|---|---|---|
| Grupo | organization_group_members | Todas as organizações que pertencem ao grupo (e, transitivamente, todos os projetos dentro dessas organizações). |
| Organização | organization_members | Uma única organização e todos os projetos dentro dela. |
| Projeto | project_members | Um único projeto apenas. |
Um usuário pode aparecer em qualquer combinação dessas tabelas. A função SQL has_org_access(auth.uid(), org_id) as avalia em conjunto e é o único predicado usado por todas as políticas de Row-Level Security no Supabase — não há lógica específica por tabela que possa divergir.
Herança de Acesso
has_org_access retorna true se qualquer uma das condições abaixo for verdadeira:
- O chamador é membro direto da organização alvo (
organization_members). - O chamador é membro do grupo da organização (
organization_group_membersjuntado viaorganizations.organization_group_id). - O chamador é membro de pelo menos um projeto dentro da organização alvo (
project_membersjuntado viaprojects.organization_id).
Isso significa que um “dono de grupo” automaticamente tem acesso a toda organização e projeto sob o grupo, sem precisar de uma segunda linha de associação por recurso. Remover a associação no grupo remove o acesso a toda a subárvore em uma única operação.
Papéis dentro de um Escopo
Cada linha de associação carrega um campo role. Papéis são locais ao escopo — ser admin de um grupo não faz de você admin de um projeto específico dentro dele, a menos que você também tenha um papel no nível do projeto. Escopos definem alcance; papéis definem o que você pode fazer dentro daquele alcance.
| Papel | Capacidades típicas |
|---|---|
owner | Controle total, incluindo transferência de propriedade e exclusão do próprio escopo. |
admin | Gerenciar membros, chaves e configurações neste escopo. |
member | Ler e usar recursos; não pode gerenciar membros ou configurações destrutivas. |
O dashboard impõe papéis via requireOrgAdmin() (owner/admin) e requireOrgAccess() (qualquer membro). O gateway usa has_org_access indiretamente — chaves de API são escopadas a uma única organização, então acesso cruzado entre orgs nunca chega ao gateway.
Row-Level Security
Toda tabela que carrega tenant no Supabase (prompts, routing_rules, organization_api_keys, feedback, etc.) tem uma política RLS na forma:
USING (has_org_access(auth.uid(), organization_id))Essa é a única verificação de acesso nessas tabelas. Acesso SQL direto através do cliente Supabase é tão seguro quanto acesso via server action pelo Zeus, porque ambos passam pela mesma política. Um token comprometido no lado do cliente ainda só pode ler linhas que o usuário subjacente já está autorizado a ler.
Rede e Transporte
| Component | Transport Security |
|---|---|
| Cliente → Gateway | HTTPS (TLS 1.2+) via CDN/load balancer |
| Gateway → Provedores | HTTPS para todos os provedores (OpenAI, Anthropic, etc.) |
| Gateway → Redis | rediss:// (TLS) em produção |
| Gateway → ClickHouse | HTTPS em produção |
| Dashboard → Supabase | HTTPS (gerenciado pelo Supabase) |
Segurança do Fluxo de Requisição
Cada requisição passa por essas camadas em ordem. Cada camada pode rejeitar a requisição de forma independente:
- Verificação do tamanho do body — requisições acima de 10MB são rejeitadas imediatamente
- Rate limit por IP — requisições não autenticadas limitadas a 20 rpm por IP
- Validação da chave de API — chave extraída, hasheada, verificada contra cache/banco de dados
- Rate limit por organização — requisições autenticadas limitadas por organização
- Verificação de assinatura — planos expirados são rejeitados com
403 SUBSCRIPTION_EXPIRED - Limites de uso — cotas mensais de requisições aplicadas, retorna
429 MONTHLY_LIMIT_EXCEEDED - Resolução de prompt — se estiver usando prompts gerenciados, variáveis de template são substituídas
- Verificação de cache — se o cache estiver habilitado, verificado antes que qualquer operação sensível chegue ao provedor
- LLM Firewall — consulta ao cache de vereditos seguida de classificacao por LLM ajustado para seguranca
- Validação de SSRF — URL de saída verificada contra lista de permissões de provedores
- Sanitização de headers — headers perigosos removidos antes do encaminhamento
- Envio ao provedor — requisição encaminhada com headers limpos e autenticação do provedor injetada
- Logging assíncrono — resposta registrada com remoção de PII, sem nunca bloquear o caminho de resposta
Garantias de Segurança do Rust
O gateway é escrito em Rust, que oferece garantias em tempo de compilação contra:
- Buffer overflows — arrays com verificação de limites, sem gerenciamento manual de memória
- Desreferência de ponteiro nulo —
Option<T>imposto pelo sistema de tipos - Data races — o borrow checker impede acesso mutável concorrente
- Use-after-free — o modelo de ownership elimina ponteiros pendentes
- Vazamentos de memória — semântica de drop determinística (sem garbage collector)
Essas não são verificações em tempo de execução — são detectadas em tempo de compilação. Código que viola a segurança de memória simplesmente não compila.
Relato de Vulnerabilidades
Se você descobrir uma vulnerabilidade de segurança, por favor reporte de forma responsável:
- Email: security@floopy.ai
- SLA de resposta: Confirmação em até 48 horas, triagem em até 7 dias
- Safe harbor: Pesquisadores agindo de boa-fé não sofrerão ações legais
- security.txt: Disponível em
https://api.floopy.ai/.well-known/security.txt
Conformidade
A arquitetura de segurança do Floopy é projetada com prontidão para conformidade em mente:
- Residência de dados — logs armazenados na região de sua escolha
- Retenção de dados — configurável por plano, aplicada via limpeza automatizada
- Trilha de auditoria — todos os eventos de segurança registrados para investigação de incidentes
- Criptografia em repouso — chaves de provedor (XChaCha20-Poly1305), dados de log (criptografia do ClickHouse)
- Criptografia em trânsito — TLS em todas as conexões
- Controle de acesso — RBAC de três camadas (grupo / organização / projeto) com permissões baseadas em papéis (owner, admin, member) e Row-Level Security em SQL via
has_org_access