Para equipes de segurança da informação e revisores de apps
Segurança no Peel
A versão curta: o conteúdo do quadro é cifrado no navegador com uma chave que vive no fragmento do link, por isso os nossos servidores guardam e retransmitem texto cifrado que não conseguem ler. Aqui está exatamente como, e onde ficam os limites.
Resumo
- O texto dos cartões, os nomes, os títulos dos quadros, os votos e os stickers são cifrados com AES-256-GCM sob uma chave de quadro gerada no navegador. A chave viaja no fragmento da URL (
#k=) e nunca é enviada a um servidor. - As ações do facilitador, o cabeçalho do quadro e os snapshots são assinados com a chave Ed25519 do facilitador. O ID do quadro é derivado dessa chave, por isso os clientes notam qualquer troca de chave.
- Os cartões ocultos ficam selados para o facilitador (sealed box X25519) até a revelação, e vinculados por compromissos com sal para que o texto revelado não possa ser trocado.
- Anonimato por padrão: as operações não levam ID de autor; votos e reações usam um ID aleatório por quadro.
- Os participantes nunca se autenticam. Sem SDKs de analytics, sem anúncios, sem scripts de terceiros, exceto o Paddle.js na página de checkout.
- Só Cloudflare. Quadros não reivindicados são apagados 60 dias após a última visita.
Como os dados circulam
O fragmento de uma URL (tudo o que vem depois do #) é tratado pelo navegador e não faz parte do pedido HTTP. É aí que o Peel coloca a chave do quadro.
Modelo de ameaças
Projetamos tudo para que um servidor totalmente comprometido ou curioso, incluindo nós, saiba o mínimo possível sobre o que uma equipe diz.
O que protegemos
- O texto dos cartões, os GIFs escolhidos e, no modo com nome, o nome de quem escreveu.
- Os nomes e avatares dos participantes, o nome do quadro e os títulos de modelos personalizados.
- Quem votou ou reagiu a quê, e quem escreveu cada cartão.
- A integridade da retro: etapas, revelações e decisões do facilitador não podem ser forjadas.
Adversários que consideramos
- Os nossos próprios servidores, alguém de dentro com más intenções ou quem invada o nosso armazenamento: obtêm texto cifrado e metadados.
- Um atacante na rede: TLS em todo o tráfego; a chave nunca atravessa a rede.
- Um servidor que troca chaves: o ID do quadro é derivado da chave pública do facilitador, e os clientes verificam o cabeçalho assinado.
- Um participante que quer espiar ou forjar: os cartões ocultos ficam selados para o facilitador; as operações do facilitador exigem a assinatura dele; editar um cartão exige a chave desse cartão.
- Um facilitador que republica por um autor offline: os compromissos o impedem de mudar o texto.
Fora do âmbito, com honestidade
- Quem tiver o link de participante pode ler o quadro. É o link que dá acesso. Compartilhe-o com o mesmo cuidado que o próprio quadro.
- Um dispositivo comprometido, uma extensão de navegador maliciosa ou alguém que olha por cima do seu ombro.
- Análise de tráfego: o servidor vê horários, tamanhos e contagens (veja o mapa de dados).
- Negação de serviço pelo servidor: ele pode descartar ou atrasar mensagens, mas não lê-las nem forjá-las.
Criptografia
Todas as primitivas vêm da API WebCrypto do navegador. Sem criptografia caseira, sem bibliotecas criptográficas de terceiros.
| Chave | Algoritmo | Usada para |
|---|---|---|
| K_board | AES-256-GCM, IV aleatório de 96 bits | Cifra cada operação, snapshot e atualização de presença. Os dados adicionais vinculam o ID do quadro, o tipo e o ID da operação, para que o texto cifrado não possa ser reaproveitado noutro quadro ou noutra posição. Vive apenas no fragmento do link #k=. |
| K_owner | Ed25519 | A autoridade do facilitador. Assina as operações do facilitador (etapa, temporizador, destaque, modelo, configurações, abertura dos votos, ocultar um cartão), o cabeçalho do quadro e os snapshots. Um contador estritamente crescente bloqueia repetições. Só no link de facilitador (&o=). |
| ID do quadro | SHA-256, primeiros 80 bits, base32 | Derivado da chave pública do facilitador, por isso o próprio ID prova a que chave o quadro pertence. Os clientes rejeitam um cabeçalho cuja chave não gere esse ID. |
| K_fac | Sealed box X25519: X25519 temporária + HKDF-SHA256 + AES-256-GCM | Os cartões ocultos são selados com a chave pública do facilitador enquanto as pessoas escrevem. Só o link de facilitador (&f=) consegue abri-los. |
| Chaves de cartão | Ed25519, uma chave por cartão | Só o autor a tem. As edições e a revelação são assinadas com ela: autenticidade sem identidade. |
| Compromissos | HMAC-SHA256 com sal aleatório de 128 bits | Um cartão oculto publica um compromisso sobre o seu conteúdo. Na revelação, o conteúdo e o sal são publicados e cada cliente os verifica, por isso ninguém consegue trocar o texto. |
| Presença | AES-256-GCM com K_board | Nomes, avatares e indicadores de escrita são cifrados como todo o resto. |
| Pacote de chaves | HKDF-SHA256 a partir do PRF da passkey ou do código de recuperação, AES-256-GCM | Facilitadores com conta podem sincronizar as chaves dos quadros entre dispositivos. O pacote é cifrado no dispositivo; o servidor guarda apenas texto cifrado e chaves encapsuladas. |
As mensagens assinadas são separadas por domínio (por exemplo peel/sig/v1, peel/header/v1, peel/snap/v1) para que uma assinatura feita para um fim não possa ser reutilizada para outro.
Anonimato
- As operações não levam ID de autor. O servidor não consegue ligar um cartão a uma pessoa e, por padrão, o facilitador também não.
- Votos e reações ficam associados a um ID aleatório por quadro, sem ligação a nenhum nome.
- Os indicadores de escrita mostram que alguém escreve, nunca o quê.
- O modo com nome é uma configuração explícita do quadro; nesse caso, o nome do autor viaja dentro do cartão cifrado.
- Os participantes nunca entram numa conta, por isso não há conta para cruzar.
Mapa de dados
Tudo o que guardamos, onde fica, quem pode ler e por quanto tempo.
| Dados | Onde | Quem pode ler | Retenção |
|---|---|---|---|
| Cartões, votos, reações, stickers, ações, nome do quadro, títulos de modelos personalizados | Armazenamento do Durable Object, como texto cifrado | Quem tem o link do quadro | Até o quadro ser apagado |
| Envelope da operação: tipo, sequência e hora do servidor, tamanho, contador do facilitador | Armazenamento do Durable Object | Peel | Até o quadro ser apagado |
| Nomes e avatares dos participantes, indicador de escrita | Retransmitidos como texto cifrado; em memória durante a conexão | Quem tem o link do quadro | Durante a conexão |
| Estado do quadro: etapa, número de participantes e de cartões, ID do modelo do catálogo, indicador de bloqueio, datas de criação e de expiração | Durable Object, D1; mensagens do Slack/Teams, se instalado | Peel; o seu canal do Slack/Teams | Duração do quadro |
| Conta de facilitador: ID do provedor, nome, e-mail se compartilhado, provedores ligados, data de criação, sessão | D1 | Peel | Até você apagar a conta |
| Pacote de chaves e chaves encapsuladas | D1, como texto cifrado | Só você (passkey ou código de recuperação) | Até você apagar a conta |
| Plano, estado da assinatura, IDs de cliente e de assinatura da Paddle | D1; Paddle | Peel, Paddle | Conforme exigido pela lei fiscal |
| IDs de workspace, canal e mensagem do Slack/Teams; token do bot (cifrado com AES-GCM) | D1 | Peel | Até a desinstalação |
| Endereço IP | Edge da Cloudflare, de forma transitória (entrega, limites de taxa) | Cloudflare, o limitador de taxa do Peel | Não guardado nas nossas bases de dados |
| Contadores diários (quadros criados, etapas alcançadas) | D1 | Peel | Apenas agregados, sem identificadores |
O que o nosso servidor pode e não pode fazer
Pode
- Ver tipos de operação, tamanhos, horários, a etapa, o número de participantes e de cartões, o ID do modelo do catálogo e o indicador de bloqueio.
- Descartar, atrasar ou recusar entregar operações (negação de serviço).
- Ver endereços IP na borda da rede.
- Contar quadros e etapas alcançadas, de forma agregada.
- Apagar um quadro (expiração, denúncias de abuso).
Não pode
- Ler o texto dos cartões, nomes, títulos de quadros, títulos de modelos personalizados, votos ou stickers.
- Ler cartões ocultos antes da revelação (nem depois).
- Forjar ações do facilitador: elas exigem a assinatura dele.
- Enfiar as suas próprias chaves: o ID do quadro denunciaria a troca.
- Mudar o texto revelado de um cartão: os compromissos e as assinaturas dos cartões denunciariam a troca.
- Saber quem escreveu cada cartão.
Como em qualquer app web, você confia no código que servimos. Mantemos essa superfície pequena: uma Content Security Policy rigorosa, script-src limitado à nossa própria origem, nenhum script de terceiros, e o protocolo fica aberto a revisão mediante pedido.
Slack e Microsoft Teams
- As mensagens levam o link do quadro e metadados (título, etapa, contagens). O texto dos cartões só é enviado quando o facilitador publica um resumo e confirma.
- O cartão de entrada contém o link de participante, que inclui a chave. Quem consegue ler esse canal pode entrar, tal como se o link tivesse sido colado lá.
- As pré-visualizações de links mostram só a etapa e as contagens, nunca conteúdo.
- Permissões do Slack (scopes):
commands,chat:write,links:read,links:write;users:readsó quando uma equipe ativa as menções. - Os tokens de bot são cifrados em repouso (AES-GCM, com a chave guardada como segredo do Worker). As assinaturas dos pedidos do Slack e os tokens do Bot Framework são verificados em cada pedido.
- Desinstalar a integração interrompe todas as publicações e apaga os tokens dela.
Web e infraestrutura
- Só Cloudflare: Pages, Workers, Durable Objects, D1, R2 e KV na conta da 1801 Labs. Nenhum outro servidor.
- TLS em todo o tráfego, HSTS e uma Content Security Policy rigorosa.
- Sem SDKs de analytics, sem anúncios, sem scripts de terceiros. A única exceção é o Paddle.js, carregado apenas na página de checkout.
- Um único cookie, só para facilitadores com conta:
peel_session, httpOnly, Secure, SameSite=Lax. - A pesquisa e o carregamento de GIFs e imagens passam pela nossa API, por isso os endereços IP dos participantes nunca chegam ao GIPHY.
- Fontes e recursos hospedados por nós. Sem fontes de CDN, sem pixels de rastreamento.
- Limites de taxa por IP e por quadro; denúncias de abuso e bloqueio pelo facilitador.
Contas e retenção
- Os participantes nunca se autenticam. Os facilitadores entram com Google (ou Entra ID através do Teams) apenas para guardar quadros ou usar funções pagas.
- Uma conta guarda um ID, o nome e o e-mail do provedor e um pacote de chaves cifrado, desbloqueado por uma passkey (WebAuthn PRF) ou por um código de recuperação. Entrar na conta dá a posse, não acesso ao conteúdo.
- Reivindicar um quadro prova a posse com uma assinatura do facilitador. As chaves dos quadros nunca são enviadas.
- Quadros sem conta são apagados 60 dias após a última visita; cada visita reinicia o prazo. Os alarmes do Durable Object apagam o armazenamento.
- Apagar a conta apaga as sessões, o pacote de chaves e a biblioteca, e devolve aos quadros a expiração de 60 dias.
Divulgação responsável
Encontrou alguma coisa? Avise-nos primeiro e trabalharemos juntos. Confirmamos que recebemos cada relato no prazo de três dias úteis.
- Dê-nos um prazo razoável (o nosso objetivo é 90 dias) para corrigir antes de divulgar.
- Teste com os seus próprios quadros e contas. Não tente obter dados de outras pessoas nem degrade o serviço.
- Não tomaremos medidas legais contra pesquisa de boa-fé que siga estas regras.
- Endereço legível por máquina:
/.well-known/security.txt.
Os clientes do plano Empresa recebem um pacote de avaliação de segurança (respostas a questionários, subprocessadores, fluxos de dados). DPA · Privacidade