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 gerador é uma aplicação de página única estática servida a partir do Cloudflare Pages: sem contas de usuário, sem autenticação e nenhum caminho de código de backend em nenhum ponto do fluxo de geração. Toda operação de geração, codificação, leitura e renderização de QR é executada inteiramente dentro do seu navegador. Um pequeno endpoint serverless existe ao lado disso e está listado aqui em vez de escondido: /api/contact (o formulário de contato, que armazena o que você envia no Cloudflare D1 e nos envia por e-mail). Ele não é chamado enquanto você cria um código QR; o verificador de segurança de URL é um produto separado em check.qr.abundera.ai. Verificar se um código QR é seguro
Modelo de ameaça
Como o gerador não coleta, armazena nem transmite dados de usuário (os dois endpoints acima tratam apenas do que você envia explicitamente a eles), 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 ele. 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 abaixo é lida do arquivo _headers implantado no momento em que esta página é gerada, então não pode divergir do que a borda envia. Verifique-a nos cabeçalhos de resposta de qualquer requisição.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com;
worker-src 'self' blob:;
style-src 'self' 'unsafe-inline' https://abundera.ai;
font-src 'self';
img-src 'self' data: blob: https:;
connect-src 'self' https: https://challenges.cloudflare.com;
frame-src 'self' https://challenges.cloudflare.com;
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' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com— sem<script>inline, semeval(). Oabundera.aié nosso próprio domínio pai e serve um único script compartilhado, o seletor de idioma. Ochallenges.cloudflare.comé o widget Turnstile usado apenas em /contact/.'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 e avatares de vCard fornecidas pelo usuário. URLs fornecidas pelo usuário nunca são executadas, apenas renderizadas. Atenção: esta diretiva também é um canal de saída: qualquer script rodando na página poderia requisitar uma imagem de qualquer host HTTPS e colocar dados na URL. Aceitamos isso porque a funcionalidade precisa disso, e preferimos dizer isso aqui a descrever a política como hermética.connect-src 'self' https: https://challenges.cloudflare.com— a diretiva que merece leitura cuidadosa.https:é uma origem de esquema: ela permite quefetch(), XMLHttpRequest, WebSocket sobre TLS, EventSource esendBeacon()alcancem qualquer origem HTTPS, não apenas as que você digitou. Ela existe para que o campo de foto do vCard e a verificação de segurança de URL possam buscar um endereço que você colou. Ela não impede exfiltração por um script malicioso; nenhuma diretiva de CSP impede isso quando o script já está rodando como nós. O que impede a exfiltração é que o código que publicamos não faz esse tipo de requisição, o que você pode observar na aba Rede.frame-src 'self' https://challenges.cloudflare.com— apenas o widget Turnstile pode ser incorporado em um frame.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. O formulário de contato é o único formulário que envia dados para algum lugar.
O que o CSP não pode fazer
O CSP limita de onde o navegador vai carregar código. Ele não avalia o que o código carregado faz. Se a nossa origem fosse comprometida, o primeiro item do modelo de ameaça, o script do atacante rodaria com as mesmas permissões que o nosso, incluindo as permissões de connect-src e img-src acima. As nossas defesas contra isso ficam a montante do CSP: os deploys saem por uma única conta Cloudflare com tokens de escopo limitado, os nomes de arquivo dos assets têm hash de conteúdo, então um arquivo trocado muda toda URL que o referencia, e o service worker faz cache apenas de respostas GET da mesma origem. O manifesto descreve o site como ele é entregue. Não é uma afirmação de que nenhuma página web jamais poderia enviar dados para algum lugar, e preferimos que você nos cobre pela afirmação mais restrita e verificável.
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 em /sw.js (o código é enviado suficientemente não minificado para acompanhar). 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