Seguridad
Nuestro modelo de seguridad en una frase: no hay nada en un servidor que atacar. Todo lo que aparece a continuación es verificable desde tus DevTools.
Arquitectura
Abundera QR es una aplicación de página única estática servida desde Cloudflare Pages. No hay servidor de aplicaciones, ni base de datos, ni cuentas de usuario, ni autenticación, ni endpoints de API, ni código de backend que procese datos de usuario. Cada operación de generación, codificación, escaneo y renderizado de QR se ejecuta completamente dentro de tu navegador.
Modelo de amenaza
Dado que no recopilamos, almacenamos ni transmitimos datos de usuario, las amenazas más comunes en aplicaciones web — robo de credenciales, brecha de base de datos, secuestro de sesión, inyección del lado del servidor — no se aplican. La superficie de ataque restante es el paquete de activos estáticos (HTML, CSS, JavaScript) servido desde nuestro origen. Diseñamos asumiendo:
- Compromiso del origen: un atacante sustituye uno de nuestros activos empaquetados por uno malicioso. El CSP que se describe a continuación limita el radio de explosión; los tokens de cache-buster facilitan la reversión a una versión conocida como válida.
- Entrada suministrada por el usuario: payloads de QR, nombres de vCard, contraseñas WiFi, filas de CSV por lotes, contenidos de imágenes escaneadas. Cada ruta de entrada se trata como no confiable cuando se renderiza de vuelta al DOM.
- Imágenes suministradas por el usuario: URLs de fotos de vCard y cargas de logotipos. Las imágenes se renderizan en un canvas, nunca se incrustan en el DOM como marcado sin procesar.
- Extensiones de navegador de terceros: fuera del alcance. Si una extensión en tu navegador tiene permiso para modificar todas las páginas, puede modificar la nuestra. Nuestras garantías se mantienen cuando la página se carga sin modificaciones.
Política de Seguridad de Contenido — por directiva
La política actual (verifica en los encabezados de respuesta de cualquier solicitud):
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'Qué permite cada directiva y dónde implica concesiones:
default-src 'self'— el piso duro. Todo lo que no relajemos explícitamente permanece en el mismo origen.script-src 'self' 'wasm-unsafe-eval'— sin<script>en línea, sineval().'wasm-unsafe-eval'es necesario por la ruta de compilación WebAssembly de nuestra librería de codificación QR; NO permite el uso tradicional deeval(). Nuestro propio control pre-despliegue escanea cada página HTML en busca de scripts en línea y falla la compilación.style-src 'self' 'unsafe-inline'— una concesión real. Nuestra vista previa de QR calcula colores a nivel de píxel en línea (atributos de estilo en módulos individuales). Una lista de permitidos basada en hash funcionaría, pero fallaría en cualquier actualización de estilo sin un despliegue. Compromiso: aceptamos la política de estilos ligeramente más débil; los estilos no pueden filtrar datos de todas formas (CSS no tiene alcance deconnect-src).img-src 'self' data: blob: https:—data:para renderizados de QR en línea,blob:para URLs de descarga de exportación,https:para URLs de fotos de vCard suministradas por el usuario. Las URLs suministradas por el usuario nunca se ejecutan, solo se renderizan.connect-src 'self' https:— fetch() está restringido a nuestro origen más las solicitudes HTTPS iniciadas por el usuario (p. ej., escanear una URL de foto). Previene la filtración mediante DNS / WebSocket / beacon a hosts arbitrarios.frame-ancestors 'none'— ningún otro sitio puede incrustarnos. Previene el clickjacking.base-uri 'self'— una etiqueta<base>inyectada de forma maliciosa no puede redirigir las URLs relativas al origen de un atacante.form-action 'self'— cualquier formulario inyectado solo puede enviar datos de vuelta a nuestro origen. No tenemos formularios que envíen datos al servidor; esto es una medida de seguridad adicional.
Se aplican diferentes políticas CSP a /bio/* (relajación de img-src para avatares suministrados por el usuario) y /embed/* (relajación de frame-ancestors para incrustación intencional). Ambas están documentadas en site/_headers.
Encabezados de transporte y marcos
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload— 1 año, todos los subdominios, elegible para la lista de precarga HSTS. Los navegadores conformes rechazan las degradaciones a HTTP. - X-Frame-Options: DENY — redundante con CSP
frame-ancestors, mantenido para compatibilidad con navegadores más antiguos. - X-Content-Type-Options: nosniff — previene ataques de confusión de MIME.
- Referrer-Policy: strict-origin-when-cross-origin — los clics en enlaces salientes filtran el origen pero no la ruta.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=()— cámara permitida solo para nuestro propio escáner; micrófono y ubicación explícitamente denegados incluso si se incrusta.
Service worker
Nuestro service worker (site/sw.js) almacena en caché solo activos del mismo origen. El controlador de fetch rechaza explícitamente las solicitudes de origen cruzado y los métodos que no son GET — puedes leer la lógica en GitHub. Las escrituras en caché están envueltas en event.waitUntil() para que no puedan descartarse en medio de una navegación.
Sanitización de entradas
Cada ruta de renderizado que acepta entrada de usuario la trata como texto no confiable:
- Las vistas previas de payload de QR usan
textContent, noinnerHTML. - Los destinos de compartición (portapapeles,
navigator.share()) pasan el texto del usuario como cadena, nunca como marcado. - Las exportaciones SVG se generan desde nuestra librería de codificación; el contenido del usuario se codifica en base64 en
<image>xlink:href, no se inyecta como elementos SVG. - Las vistas previas de impresión usan una URL blob, no
document.write(). - El análisis de localStorage está envuelto en
try/catch— una entrada corrupta produce un valor predeterminado vacío nuevo, nunca una excepción que pueda desenrollarse en una ruta de código activa. - Las URLs suministradas por el usuario que se muestran como botones "Abrir enlace" están restringidas a
http(s)://— los esquemasjavascript:ydata:son rechazados.
Obtención de imágenes de origen cruzado
Cuando un usuario pega una URL https: como foto de vCard o logotipo, el navegador la obtiene sujeto a CORS y la lista de permitidos img-src de nuestro CSP. La imagen se renderiza en un canvas. Nunca se convierte en DOM activo, nunca se ejecuta como código y nunca llega a nuestro origen — la obtención es navegador → imagen remota, y el resultado se pinta en el lado del cliente. Un atacante que controla una URL de imagen remota puede rastrear que la URL fue cargada (una línea de registro en su propio servidor), pero no puede filtrar nada de nuestra página.
Integridad de Subrecursos (SRI)
Todo el JavaScript y CSS que enviamos es del mismo origen. No cargamos scripts ni hojas de estilo de terceros, por lo que los hashes SRI no son aplicables. Si alguna vez cargamos un activo de terceros, incluiremos un atributo integrity SRI en él y documentaremos el proceso de actualización de hash en esta página.
Reportar una vulnerabilidad
Si descubres un problema de seguridad que afecta a Abundera QR — ya sea en nuestro código, nuestro despliegue o en una dependencia que enviamos — por favor repórtalo de forma privada a security@abundera.ai. Nuestro objetivo es triagiar en 72 horas. También puedes contactarnos a través de los detalles de contacto en nuestro archivo /.well-known/security.txt.
Sin programa de recompensas (por ahora)
Actualmente no ofrecemos recompensas pagadas, pero cada informe válido confirmado recibe crédito en el registro de cambios y nuestro agradecimiento público.
Verifica cualquiera de lo anterior
Cada afirmación en esta página es verificable desde los DevTools de tu navegador sin necesidad de confiarnos:
- CSP: DevTools → Red →
/→ Encabezados. Lee el encabezado de respuestaContent-Security-Policy. - HSTS + encabezados de seguridad: mismo lugar — todos
Strict-Transport-Security,X-Frame-Options, etc. son visibles. - Sin llamadas salientes: DevTools → Red → Fetch/XHR. Genera un QR. Observa cómo el contador se mantiene en cero.
- Alcance del service worker: DevTools → Aplicación → Service Workers. Verifica la fuente del script y la lista de activos en caché.
- Sin cookies: DevTools → Aplicación → Cookies. Vacío.
- Guía completa: la sección de verificación del manifiesto.
Contacto
Divulgaciones de seguridad: security@abundera.ai