Hoje estamos no Product Hunt! Deixar um comentário
O Authenticator 1.16.0 chegou. Ver as novidades
Uma visão técnica para quem avalia segurança. Tudo aqui pode ser conferido no código-fonte — inclusive as partes menos favoráveis, que declaramos em vez de omitir.
A extensão é uma extensão do Chrome em Manifest V3 que gera códigos TOTP localmente com OTPAuth. Gerar códigos, guardar contas e cifrá-las não envolve backend nenhum, e não existem contas de usuário. Mantemos um único servidor, api.authenticator.sh, para o plano gratuito e o Pro — descrito abaixo —, e ele nunca recebe um segredo, um nome de conta, um emissor ou um código.
Esse servidor não guarda material de chave das contas de ninguém: não há custódia de chaves nem qualquer caminho de recuperação sob nosso controle. Se alguém esquece a senha e perde o código de recuperação, não temos como ajudar — é proposital.
A proteção por senha é opcional e vem desativada. Com ela ativada:
Os dados nunca são cifrados diretamente com uma chave derivada da senha. Uma chave mestra aleatória cifra os registros; a saída do PBKDF2 apenas encapsula essa chave mestra. Duas consequências decorrem daí, e as duas importam:
Com a proteção por senha ativada, todo o registro da conta é cifrado, exceto seu identificador e sua impressão digital — inclusive o nome do serviço, de modo que um perfil roubado não revela em quais serviços o usuário tem conta. Ao ativar, a extensão cifra os registros, verifica-os decifrando e comparando e só então remove todas as cópias em texto claro: armazenamento local do navegador, sincronização e os sete instantâneos rotativos de backup.
Dito sem rodeios, porque pesa na sua avaliação:
Em resumo: isto protege dados em repouso. Não defende, nem tem como defender, um dispositivo que já esteja sob controle de um atacante durante o uso.
O manifesto declara cinco permissões e nenhuma permissão de host:
Não há Não há content scripts, nem permissões tabs, cookies ou webRequest, nem web-accessible resources. Nada nosso existe em uma página até você pedir um código ali; o que roda então é injetado para aquela aba e aquela invocação, sob a autorização que o seu próprio clique concede, e não deixa nada para trás.
A leitura de QR pela câmera roda em uma página da extensão aberta numa aba, não no popup — um popup é destruído ao perder o foco, que é exatamente o que um pedido de permissão faz. Para isso não é preciso nenhuma permissão no manifesto: usa-se o pedido de câmera padrão do navegador, concedido por origem da extensão e revogável nas configurações do site, e ele nunca aparece antes de o usuário abrir o leitor. O vídeo é decodificado localmente e nunca é transmitido nem armazenado.
Desde a 1.14.0, a extensão faz um check-in com api.authenticator.sh uma vez por dia — quando o navegador inicia ou depois que o popup é renderizado, nunca no caminho até exibir um código. A requisição é uma lista fechada de campos, montada em uma única função em src/billing/sync.ts e mantida nessa lista por um teste:
Nunca enviados: segredos, nomes de contas, emissores, códigos ou os sites em que eles são usados. O servidor obtém o país a partir da requisição, na borda, e não armazena o endereço IP. A página de desinstalação recebe o mesmo ID de instalação como ?i=: ela informa ao servidor que a instalação foi removida e vincula a ela uma resposta opcional à pesquisa.
Guardado em chrome.storage.local para isso: o ID de instalação, a última política e a última licença assinadas, a chave de licença que esta instalação tem (digitada, ou recebida depois de uma compra feita nela), as contagens diárias de aberturas até o servidor recebê-las e os eventos na fila — nenhum dado de conta. Com a sincronização ativada, a informação de que a instalação é anterior à cobrança e essa chave de licença também vão para o Chrome Sync, para que os outros perfis do Chrome do usuário as recebam e ativem o Pro sozinhos; desativar a sincronização remove as duas do Chrome Sync, e reativá-la as coloca de volta.
O que volta e como a extensão trata isso:
O limite não é DRM: o código é aberto, e um fork pode removê-lo. O que nunca pode ser possível é alguém obter ou usar a licença de outra pessoa, ou fazer com que a instalação de outra pessoa fique limitada — isso é uma vulnerabilidade e está no escopo da nossa política de segurança.
Dois tipos de requisição acontecem por conta própria: a verificação do relógio e o check-in diário de cobrança. Todo o resto abaixo acontece porque o usuário clicou em algo. O Manifest V3 proíbe código remoto, e nada aqui leva contas, chaves secretas ou códigos gerados. A lista completa:
| Host | Quando | Leva dados do usuário? |
|---|---|---|
| time.akamai.com timeapi.io cloudflare.com | Verificação de desvio do relógio, em cache; se nenhuma fonte responder, as Configurações avisam. Com o relógio desajustado o TOTP quebra. | Não |
| authenticator.sh | Página de boas-vindas na instalação; página de feedback na desinstalação, com o ID de instalação no endereço; páginas de ajuda e suporte quando abertas pelo popup. | O ID de instalação, só na página de desinstalação; fora isso, não (logs comuns de servidor web) |
| api.authenticator.sh | Check-in de cobrança uma vez por dia; chamadas de checkout, licença e portal quando o usuário compra o Pro, digita uma chave, restaura uma compra ou gerencia uma assinatura. | Os campos listados acima — nunca dados de conta. Uma chave de licença ou um endereço de e-mail só quando o usuário digita um |
| checkout.stripe.com billing.stripe.com | Stripe Checkout e portal do cliente, abertos em uma aba quando o usuário compra o Pro ou gerencia uma assinatura. | O que o comprador digitar lá. Valem os termos da própria Stripe e do Link |
| chromewebstore.google.com | Página da loja, aberta em uma aba ao clicar: o formulário de avaliação vindo do aviso de avaliação. | Não (o Google vê a visita a uma página da loja como qualquer outra) |
Nenhuma fonte, script, folha de estilo ou imagem é carregada de hosts de terceiros. Tudo o que a interface precisa para ser renderizada vem dentro do pacote.
Nada do que está acima precisa ser aceito como fé. A extensão é de código aberto e cada versão é reproduzível:
.crx publicado na Chrome Web Store e descompacte-onpm ci && npm run build no Node 20 LTSdist/ resultante com o pacote descompactadoA cada release no GitHub publicamos um SHA-256 de cada arquivo do dist/ produzido, como SHA256SUMS-v<version>.txt e no formato sha256sum, de modo que o passo 4 vira uma única execução de sha256sum -c. As diferenças devem se limitar à ordem dos arquivos dentro dos arquivos compactados e a espaços na saída minificada entre versões de patch do Node.
As dependências de execução são propositalmente poucas — OTPAuth, React, jsQR, Zustand, Framer Motion e Lucide — fixadas por um lockfile versionado. Um SBOM CycloneDX está disponível mediante solicitação.
Ver o código-fonteSomos um fornecedor pequeno e independente. Se algum dos itens abaixo for requisito obrigatório no seu processo, é melhor saber agora do que três semanas depois de iniciada a avaliação:
O que oferecemos em troca é transparência total do código, uma build verificável, uma superfície mínima de permissões e um servidor que, para começo de conversa, nunca recebe seus segredos, nomes de contas ou códigos.
Relatos de vulnerabilidade e dúvidas de segurança: security@authenticator.sh. Nossa política de divulgação, escopo, prazos de resposta e termos de safe harbour estão na página de política de segurança; o que é armazenado e o que sai do dispositivo está descrito na política de privacidade.
Temos prazer em responder a um questionário escrito de avaliação de fornecedores ou percorrer o código junto com uma equipe de segurança.
Para qualquer assunto que não seja de segurança — instalação, recuperação de conta, dúvidas de cobrança em uma implantação — veja o suporte. Por favor, não envie relatos de vulnerabilidade para lá: aquela caixa não é tratada como confidencial.