Segurança

Como mantemos seus apps privados e separados

Os criadores não escrevem nada disso. Login, acesso só para a empresa, dados separados para cada app e ficar fora dos buscadores vêm com cada app na primeira publicação. Aqui está a versão detalhada, para quem precisa aprovar. O TeamOnly está em acesso antecipado. Esta página descreve como ele é construído e funciona hoje, e mantemos o texto exato conforme isso muda. Não alegamos certificações que não temos.

Toda requisição passa pelo login primeiro

Todo o tráfego dos apps em *.teamonly.app vai para um único Worker de despacho antes de qualquer outra coisa. Ele identifica a organização e o app pelo hostname, verifica se há uma sessão válida e só então encaminha a requisição para o app. Sem sessão, o visitante recebe a tela de login. O código do cliente nunca roda para ele.

Há três formas de entrar: OpenID Connect do Google, OpenID Connect da Microsoft (Entra ID) ou um código de uso único enviado a um e-mail da empresa. No Google e na Microsoft, o ID token é verificado no nosso servidor, incluindo assinatura, audiência, expiração e o domínio do e-mail. O valor hd do Google é conferido a partir do token verificado, não aceito da requisição. Na Microsoft, só contas corporativas e de estudante são aceitas, e o emissor do token precisa corresponder ao próprio tenant.

Códigos por e-mail

Em vez de Google ou Microsoft, um colega pode pedir um código de 6 dígitos no endereço de trabalho. É assim que empresas cujo e-mail não está no Google Workspace nem no Microsoft 365 usam o TeamOnly, sem configurar nada. O código prova que a pessoa controla uma caixa de e-mail num dos domínios da sua empresa, o mesmo que um login com Google ou Microsoft prova. Funciona com qualquer provedor de e-mail. Endereços pessoais como Gmail ou Outlook.com são recusados para apps de empresa.

  • Um código expira depois de 10 minutos.
  • Depois de cinco tentativas erradas, o código para de funcionar. A pessoa precisa pedir um novo.
  • Um código só funciona no navegador que pediu. Um código visto por cima do ombro de alguém, ou encaminhado, não serve em outro navegador.
  • Os pedidos têm limite por endereço de e-mail e por endereço IP, então ninguém consegue usar o formulário para lotar uma caixa de entrada.
  • O formulário é protegido pelo Cloudflare Turnstile, que verifica se quem pede é uma pessoa, não um script.
  • O formulário dá a mesma resposta quer um endereço possa entrar ou não, então não revela quem tem acesso.
  • Os códigos são enviados de signin@teamonly.app, com SPF, DKIM e DMARC configurados para que os provedores de e-mail confirmem que vieram mesmo de nós.
  • Os códigos são guardados só como hash, e o acesso é verificado de novo quando o código é digitado, caso tenha mudado nesse meio-tempo.

Uma identidade em que seu app pode confiar

O Worker de despacho remove qualquer cabeçalho de identidade da requisição recebida e depois define ele mesmo o e-mail e o nome verificados, junto com os papéis da pessoa naquele app. O SDK entrega esses dados ao seu app como ctx.user.

Na porta
incoming request to expenses.acme.teamonly.appx-teamonly-user: ceo@acme.com    removed, not from usx-teamonly-user: dana@acme.com   added from verified sign-in
Nomes de cabeçalho ilustrativos. Seu app lê o usuário pelo SDK, não pelos cabeçalhos.

Banco de dados e armazenamento de arquivos separados por app

Cada app ganha seu próprio banco de dados D1, criado na primeira publicação, e seu próprio prefixo de armazenamento R2 para arquivos. Esses são os únicos bindings de dados ligados ao app. Os apps rodam como scripts separados num dispatch namespace do Workers for Platforms e não alcançam os bindings uns dos outros.

Testamos isso de propósito: um app tentando ler dados, cookies ou segredos de outro app. Esses testes foram feitos na plataforma em produção, e rodamos de novo sempre que mudam as partes que mantêm os apps separados.

Uma planta com três salas: os apps de despesas, estoque e consulta. Cada sala guarda seus próprios dados, arquivos e segredos. O app de despesas tenta entrar na sala do estoque e bate na parede. O app de estoque tenta entrar na sala da consulta e também bate na parede. Dados, cookies e segredos ficam na própria sala.

Cada app fica na sua própria sala. Dados, arquivos, cookies e segredos ficam lá dentro.

Segredos guardados fora do seu código

Os segredos são definidos com npx teamonly secrets set e guardados como secrets criptografados do Worker, ligados àquele único app. Eles não ficam no seu repositório, nem no pacote, nem visíveis para outros apps. O arquivo de skill diz à sua IA para nunca colocar chaves no código.

Cookies limitados ao endereço de um app

Os cookies de sessão são definidos para o hostname exato do app, como expenses.acme.teamonly.app, com Secure, HttpOnly e SameSite. Eles nunca são definidos em teamonly.app nem no subdomínio da organização, então um app não consegue ler a sessão de outro.

teamonly.app ainda não está na Public Suffix List, então os navegadores tratam todos os endereços de apps como parte de um mesmo site quando decidem o que é same-site. Por isso cada cookie de sessão fica preso ao endereço exato do seu app. Esta página vai avisar se isso mudar.

O código do cliente é tratado como não confiável

Partimos do princípio de que qualquer app pode ter erros ou código malicioso, porque parte dele foi escrita por IA e qualquer pessoa pode errar. É por isso que a autenticação fica fora do app, que cada app recebe só os próprios recursos e que nada que um app envie consegue mudar quem está autenticado. É também por isso que um app só pode mandar e-mail para pessoas da sua empresa, até 100 destinatários por hora e 500 por dia.

Onde roda

Tudo roda na Cloudflare: Workers e Workers for Platforms para código, D1 para bancos de dados, R2 para arquivos e KV para sessões. Não temos servidores próprios. A escolha da região dos dados está incluída no plano Business, e configuramos com você na entrada.

Configurado com você, e o que nunca fazemos

No plano Business, configuramos o Okta ou outro provedor de SSO com sua equipe de TI. Logs de auditoria e a região dos dados são configurados com você da mesma forma. Eles estão incluídos no plano, não são opções que você liga sozinho.

  • Configurado com você
    Okta, OneLogin e outros SSO (plano Business)
  • Configurado com você
    Logs de auditoria (plano Business)
  • Configurado com você
    Escolha da região dos dados (plano Business)
  • Nunca
    Apps públicos, sem autenticação

Hoje não temos certificação SOC 2 nem ISO.

Como relatar um problema de segurança

Escreva para hello@teamonly.dev com “Security” no assunto. Uma pessoa lê cada mensagem, e vamos responder confirmando o recebimento. Pedimos um prazo razoável para corrigir o problema antes de você divulgá-lo.

FAQ

Perguntas frequentes

Todas as perguntas e respostas

Um app consegue ler o banco de dados de outro?

Não. Cada app recebe acesso só ao próprio banco de dados, armazenamento de arquivos e segredos. Ele não tem credenciais nem bindings para os recursos de nenhum outro app, então não há a que se conectar.

Um app consegue se passar por outro usuário?

Nem para si mesmo nem para nós. Os cabeçalhos de identidade das requisições recebidas são removidos na porta e substituídos pelos verificados, então um visitante não consegue fingir ser outra pessoa. A identidade que um app vê sempre vem de um login real.

Os apps são indexados por buscadores?

Não. Todo app fica atrás do login, então os robôs de busca só chegam à tela de login, e toda resposta, incluindo essa tela, envia noindex.

Acesso antecipado

Coloque sua primeira ferramenta no ar esta semana.

O TeamOnly está em acesso antecipado, e todos os planos já estão abertos. Teste grátis por 14 dias sem cartão, ou assine. Nos dois casos, sua empresa fica pronta na hora, e três comandos colocam seu primeiro app no ar.

Teste grátis por 14 diasOu assine agora

Sem cartão no teste. Depois, a partir de $19 por mês, cancele quando quiser.

Ainda não? Receba novidades

Use seu e-mail corporativo. Escrevemos pouco, e só quando há novidade que valha a pena.

Dúvidas vão para hello@teamonly.dev, e uma pessoa responde.