Segurança
Nosso modelo de segurança em uma frase: não há nada em um servidor para atacar. Tudo abaixo é verificável a partir das suas DevTools.
Arquitetura
O Abundera QR é uma aplicação de página única estática servida a partir do Cloudflare Pages. Não há servidor de aplicação, banco de dados, contas de usuário, autenticação, endpoints de API e nenhum caminho de código de backend que processe dados do usuário. Toda operação de geração, codificação, leitura e renderização de QR é executada inteiramente dentro do seu navegador.
Modelo de ameaça
Como não coletamos, armazenamos nem transmitimos dados de usuário, as ameaças mais comuns em aplicações web — roubo de credenciais, violação de banco de dados, sequestro de sessão, injeção no lado do servidor — não se aplicam. A superfície de ataque restante é o bundle de assets estáticos (HTML, CSS, JavaScript) servido de nossa origem. Projetamos assumindo:
- Comprometimento da origem: um atacante substitui um de nossos assets empacotados por um malicioso. O CSP descrito abaixo limita o raio de explosão; tokens cache-buster facilitam o rollback para uma versão conhecida como válida.
- Entrada fornecida pelo usuário: payloads de QR, nomes de vCard, senhas WiFi, linhas de CSV em lote, conteúdos de imagens escaneadas. Todo caminho de entrada é tratado como não confiável quando renderizado de volta ao DOM.
- Imagens fornecidas pelo usuário: URLs de fotos de vCard e uploads de logotipos. As imagens são renderizadas em um canvas, nunca incorporadas ao DOM como marcação bruta.
- Extensões de navegador de terceiros: fora do escopo. Se uma extensão no seu navegador tiver permissão para modificar cada página, ela pode modificar a nossa. Nossas garantias se mantêm quando a página é carregada sem modificações.
Política de Segurança de Conteúdo — por diretiva
A política atual (verifique nos cabeçalhos de resposta de qualquer requisição):
Content-Security-Policy:
default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
worker-src 'self' blob:;
style-src 'self' 'unsafe-inline';
font-src 'self';
img-src 'self' data: blob: https:;
connect-src 'self' https:;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'O que cada diretiva nos permite fazer e onde ela representa um compromisso:
default-src 'self'— o piso rígido. Tudo que não relaxamos explicitamente permanece na mesma origem.script-src 'self' 'wasm-unsafe-eval'— sem<script>inline, semeval().'wasm-unsafe-eval'é necessário pelo caminho de compilação WebAssembly da nossa biblioteca de codificação QR; ele NÃO permite oeval()tradicional. Nossa própria verificação pré-deploy escaneia cada página HTML em busca de scripts inline e falha o build.style-src 'self' 'unsafe-inline'— uma concessão real. Nossa pré-visualização de QR calcula cores em nível de pixel inline (atributos de estilo em módulos individuais). Uma lista de permissões baseada em hash funcionaria, mas falharia em qualquer atualização de estilo sem um deploy. Compromisso: aceitamos a política de estilos ligeiramente mais fraca; estilos não conseguem exfiltrar dados de qualquer forma (CSS não tem alcance deconnect-src).img-src 'self' data: blob: https:—data:para renderizações de QR inline,blob:para URLs de download de exportação,https:para URLs de fotos de vCard fornecidas pelo usuário. URLs fornecidas pelo usuário nunca são executadas, apenas renderizadas.connect-src 'self' https:— fetch() é restrito à nossa origem mais requisições HTTPS iniciadas pelo usuário (ex.: escanear uma URL de foto). Previne exfiltração via DNS / WebSocket / beacon para hosts arbitrários.frame-ancestors 'none'— nenhum outro site pode nos incorporar. Previne clickjacking.base-uri 'self'— uma tag<base>injetada de forma maliciosa não pode redirecionar URLs relativas para a origem de um atacante.form-action 'self'— qualquer formulário injetado só pode postar de volta para nossa origem. Não temos formulários que enviam dados ao servidor; isto é uma medida de segurança adicional.
Políticas CSP diferentes se aplicam a /bio/* (img-src relaxado para avatares fornecidos pelo usuário) e /embed/* (frame-ancestors relaxado para incorporação intencional). Ambas estão documentadas em site/_headers.
Cabeçalhos de transporte e enquadramento
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload— 1 ano, todos os subdomínios, elegível para a lista de pré-carregamento HSTS. Downgrades para HTTP são recusados por navegadores conformes. - X-Frame-Options: DENY — redundante com CSP
frame-ancestors, mantido para cobertura de navegadores mais antigos. - X-Content-Type-Options: nosniff — previne ataques de confusão de MIME.
- Referrer-Policy: strict-origin-when-cross-origin — cliques em links de saída revelam a origem, mas não o caminho.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=()— câmera permitida apenas para nosso próprio scanner; microfone e localização explicitamente negados mesmo se incorporado.
Service worker
Nosso service worker (site/sw.js) armazena em cache apenas assets da mesma origem. O handler de fetch rejeita explicitamente requisições de origem cruzada e métodos não-GET — você pode ler a lógica no GitHub. Escritas em cache são envolvidas em event.waitUntil() para que não possam ser descartadas no meio de uma navegação.
Sanitização de entradas
Todo caminho de renderização que aceita entrada do usuário a trata como texto não confiável:
- Pré-visualizações de payload de QR usam
textContent, nãoinnerHTML. - Destinos de compartilhamento (área de transferência,
navigator.share()) passam texto do usuário como uma string, nunca como marcação. - Exportações SVG são geradas pela nossa biblioteca de codificação; o conteúdo do usuário é codificado em base64 em
<image>xlink:href, não injetado como elementos SVG. - Pré-visualizações de impressão usam uma URL blob, não
document.write(). - O parsing de localStorage é envolvido em
try/catch— uma entrada corrompida produz um novo padrão vazio, nunca uma exceção que poderia se desenrolar em um caminho de código ativo. - URLs fornecidas pelo usuário exibidas como botões "Abrir link" são restritas a
http(s)://— esquemasjavascript:edata:são rejeitados.
Busca de imagens de origem cruzada
Quando um usuário cola uma URL https: como foto de vCard ou logotipo, o navegador a busca sujeito ao CORS e à lista de permissões img-src do nosso CSP. A imagem é renderizada em um canvas. Ela nunca se torna DOM ativo, nunca é executada como código e nunca chega à nossa origem — a busca é navegador → imagem remota, e o resultado é pintado no lado do cliente. Um atacante que controla uma URL de imagem remota pode rastrear que a URL foi carregada (uma linha de log em seu próprio servidor), mas não consegue exfiltrar nada da nossa página.
Integridade de Sub-Recursos (SRI)
Todo JavaScript e CSS que entregamos é da mesma origem. Não carregamos scripts ou folhas de estilo de terceiros, portanto os hashes SRI não são aplicáveis. Se algum dia carregarmos um asset de terceiros, incluiremos um atributo integrity SRI nele e documentaremos o processo de atualização de hash nesta página.
Reportar uma vulnerabilidade
Se você descobrir um problema de segurança afetando o Abundera QR — seja no nosso código, nossa implantação ou em uma dependência que entregamos — por favor, reporte-o de forma privada para security@abundera.ai. Buscamos fazer a triagem em 72 horas. Você também pode nos contatar através dos detalhes de contato no nosso arquivo /.well-known/security.txt.
Sem programa de bug bounty (ainda)
Atualmente não oferecemos recompensas pagas, mas cada relatório válido confirmado recebe crédito no changelog e nosso agradecimento público.
Verifique qualquer uma das afirmações acima
Cada afirmação nesta página é falsificável a partir das DevTools do seu navegador sem precisar confiar em nós:
- CSP: DevTools → Rede →
/→ Cabeçalhos. Leia o cabeçalho de respostaContent-Security-Policy. - HSTS + cabeçalhos de segurança: mesmo lugar — todos
Strict-Transport-Security,X-Frame-Options, etc. são visíveis. - Sem chamadas de saída: DevTools → Rede → Fetch/XHR. Gere um QR. Observe o contador permanecer em zero.
- Escopo do service worker: DevTools → Aplicação → Service Workers. Verifique a fonte do script e a lista de assets em cache.
- Sem cookies: DevTools → Aplicação → Cookies. Vazio.
- Guia completo: a seção de verificação do manifesto.
Contato
Divulgações de segurança: security@abundera.ai