Segurança

Como o sistema é construído, onde ele roda e o que ele deliberadamente não faz.

Toda ferramenta gratuita de PDF na internet abre com “seu arquivo nunca sai do navegador”. Esta não abre, porque não pode — e uma página que fugisse disso seria a página errada para ter num site sobre segurança. Então a versão honesta vem primeiro, e todo o resto é o detalhe por trás dela.

Por que isto roda num servidor

Um verificador que roda só no navegador é um desenho legítimo e bom, e ele compra exatamente uma propriedade: os bytes ficam na sua máquina. O que ele custa é profundidade. A análise aqui percorre as tabelas de referência cruzada, reexecuta os fluxos de conteúdo das páginas para reconstruir a ordem de pintura, decodifica o texto coberto pelo mapa /ToUnicode da fonte e compara o documento com perfis estruturais montados a partir de espécimes legítimos de emissores. Isso é um parser com orçamento rígido de CPU e um botão de desligar, não algo para rodar dentro da aba de quem por acaso abriu a página.

A troca é deliberada: o arquivo é enviado por TLS e, em troca, a resposta é uma que nenhum leitor de metadados no navegador consegue produzir. O que devemos a você por essa troca é que a estadia do arquivo seja a mais curta e a mais contida possível — que é o resto desta página.

Se essa troca é errada para o seu documento, não faça a troca. Um processo sob segredo de justiça, um incidente em andamento, uma peça ainda sem tarja antes do protocolo — para esses, a ferramenta certa é uma que rode na sua própria máquina, e hoje esta não é. Preferimos dizer isso aqui a você descobrir depois.

O que acontece com um documento enviado

A vida inteira de um arquivo enviado, em ordem:

  1. Ele chega por TLS e é mantido em memória. Não há arquivo temporário, diretório de upload, object store nem fila atrás do endpoint.
  2. O tipo é decidido farejando os bytes, nunca pelo cabeçalho Content-Type nem pelo nome do arquivo — então um chamador hostil não escolhe qual parser roda na entrada dele.
  3. O buffer vai para uma thread de trabalho, uma de duas, com prazo rígido de 15 segundos. Um arquivo que esgota esse prazo tem sua thread morta e substituída, e o chamador recebe 422 analysis_timeout. Quando as duas estão ocupadas e a fila está cheia, a resposta é 503 busy, e não uma latência que só cresce.
  4. O relatório é serializado e devolvido. O buffer é descartado. Nada do documento sobrevive à resposta — nem os bytes, nem o texto extraído, nem o nome do arquivo.
  5. Uma linha de registro é gravada, e ela não guarda documento nenhum: o id da chave, o id da conta, o tamanho em bytes, a faixa de risco e um carimbo de tempo. É isso que mede a sua cota.

Os envios têm teto de 10 MB. Todas as outras rotas da origem têm teto de 64 KB, então nada além do único endpoint que precisa de folga pode ser levado a bufferizar megabytes antes de o handler rodar.

O texto recuperado — as palavras encontradas embaixo de uma tarja mal aplicada — volta para você, na resposta, e não é guardado em lugar nenhum. Ele é o achado, então omiti-lo tornaria o relatório inútil; e guardá-lo faria deste o banco de dados mais sensível do produto. Ele não é registrado em log nem persistido.

A política de privacidade é a versão coluna por coluna do que o banco guarda.

Onde ele roda

  • Um servidor, na Finlândia. Uma instância Hetzner em Helsinque (hel1). Computação e banco de dados ficam na UE.
  • Nenhuma porta de entrada é publicada. A origem não é alcançável pela internet. O tráfego chega por um Cloudflare Tunnel, que abre uma conexão de saída a partir da máquina; não há socket escutando para alguém escanear, nem uma regra de firewall entre você e um erro.
  • TLS em todo o caminho. Terminado na borda da Cloudflare e levado até a origem dentro do túnel. HTTP é redirecionado, e o host canônico é tamperlens.com.
  • O banco é um único arquivo SQLite no disco dessa máquina, com contas, hashes de chaves, sessões e o registro de uso. Ele nunca guardou um documento, porque nenhum caminho de código grava um.

Endurecimento da aplicação

  • Uma Content-Security-Policy de verdade. default-src 'self' e nenhum 'unsafe-inline' em lugar algum do site. Toda página que escrevemos carrega script e folha de estilo como arquivos externos, e a CI quebra o build se aparecer script ou estilo embutido em src/web. A única exceção é /docs, onde o Swagger UI injeta o próprio bootstrap e não oferece como desligá-lo; fica restrito a esse único prefixo, que não renderiza conteúdo de usuário.
  • Envios de outra origem são recusados. Um navegador só pode chamar /inspect a partir das nossas páginas. Requisições sem cabeçalho Origin — curl, SDKs de servidor — não são afetadas, porque gastam o próprio endereço em vez de pegar emprestado o de um visitante.
  • Limites de taxa por rede, não por cabeçalho. Chamadores anônimos têm 10 inspeções por hora e 25 por dia por /64 IPv6 (um cliente é um balde, não 264 deles), sob um teto diário de toda a instalação. Chamadores com chave são medidos pela cota mensal.
  • Outros cabeçalhos: Referrer-Policy: no-referrer, Cross-Origin-Opener-Policy: same-origin, X-Content-Type-Options: nosniff, HSTS em produção e frame-ancestors 'self'.
  • PDFs criptografados são declarados, não atacados. Um arquivo protegido por senha é reportado como “sem análise de conteúdo”, e não varrido como texto cifrado e reportado como limpo. Não tentamos quebrar a proteção de nada que você enviar.

Contas, chaves e segredos

  • Chaves de API são guardadas como hashes SHA-256. A chave em si existe nos nossos sistemas apenas pelo tempo de mostrá-la a você uma vez. É também por isso que não conseguimos reenviá-la — a rotação emite uma nova e invalida a antiga.
  • Senhas são hashes scrypt com sal aleatório por conta. O texto puro nunca é guardado e o hash não é reversível.
  • Sessões são HttpOnly, Secure e SameSite=Lax, expiram em 30 dias e são apagadas no logout. Redefinir a senha invalida todas as outras sessões.
  • Dados de cartão nunca chegam até nós. O checkout e o portal de cobrança são hospedados pela Stripe; guardamos um id opaco de cliente e mais nada.
  • Uma chave, um inquilino. Perfis privados de emissor resolvem apenas contra a conta dona — não existe caminho de código da requisição de uma conta até o perfil de outra.

Disponibilidade e recuperação

  • Deploys são condicionados e reversíveis. Um push para main roda typecheck, build e a suíte de testes inteira; só no verde é que um runner na máquina reconstrói, espera o healthcheck do contêiner e reverte automaticamente se o build novo não estiver saudável.
  • Backups do banco a cada seis horas, 48 cópias retidas (doze dias). Elas não contêm documentos, porque o banco não contém.
  • A disponibilidade é sondada de fora a cada 15 minutos, e os erros vão para o Sentry com o caminho da requisição — nunca com um documento.

Quem mais encosta nisso

A lista completa. Nenhum deles recebe um documento enviado, porque o documento não sobrevive à requisição que o trouxe.

Provedor O que faz O que enxerga
Cloudflare DNS, TLS, entrada por túnel, desafio antibot Metadados da requisição e endereço IP, em trânsito
Hetzner O servidor e o disco dele A máquina física; nenhum acesso em nível de aplicação
Stripe Checkout, assinaturas, portal de cobrança Seus dados de cobrança. Não vemos dados de cartão
Resend E-mail de confirmação e de redefinição de senha Seu endereço de e-mail e a mensagem
Sentry Relato de erros Stack traces e caminhos de requisição
GitHub Código, CI, disparo do deploy Nada sobre você; nenhum dado de produção

O que não temos

Uma página de segurança que lista só qualidades é publicidade. Estas são as perguntas de uma análise de fornecedor cuja resposta hoje é não.

  • Sem SOC 2, ISO 27001 ou atestado HIPAA. A Tamperlens é um produto pequeno e independente, e não foi auditada contra nenhum deles. Se o seu processo exige fornecedor certificado, este ainda não é um.
  • Sem redundância entre regiões. Um servidor, uma região. Uma falha de hardware é indisponibilidade, não perda de dados — mas é indisponibilidade.
  • Sem chaves gerenciadas pelo cliente e sem versão on-premise. Hoje não há como rodar o motor dentro da sua própria rede.
  • Sem relatório de teste de intrusão para compartilhar. O endurecimento acima é real e boa parte dele é garantida por testes na CI; nada disso foi revisado por avaliador externo.
  • Forense de metadados é indício, não prova. Um PDF regerado do zero lava todos os sinais de nível de bytes, e exiftool -all= remove todo achado de identidade em imagens. Um acerto é indício forte; uma ausência não é indício nenhum — e todo achado diz isso no próprio texto.

Como relatar uma vulnerabilidade

Escreva para [email protected] com detalhe suficiente para reproduzir. Você receberá resposta de uma pessoa. Não há programa de recompensa e não há ameaça jurídica: relate de boa-fé, dê uma janela razoável para a correção, e damos o crédito a você se quiser o crédito.

Por favor, não rode scanners automáticos contra o host de produção. Os limites de taxa acima simplesmente vão recusar você, e o barulho enterra justamente o relato que você queria fazer.