Sicurezza
Il nostro modello di sicurezza in una frase: non c'è nulla su un server da attaccare. Tutto ciò che segue è verificabile dai tuoi DevTools.
Architettura
Abundera QR è un'applicazione a pagina singola statica distribuita da Cloudflare Pages. Non esiste alcun server applicativo, nessun database, nessun account utente, nessuna autenticazione, nessun endpoint API e nessun percorso di codice backend che elabori dati utente. Ogni operazione di generazione, codifica, scansione e rendering QR viene eseguita interamente nel tuo browser.
Modello di minaccia
Poiché non raccogliamo, archiviamo o trasmettiamo dati utente, le minacce più comuni alle app web, furto di credenziali, violazione del database, hijacking di sessione, iniezione lato server, non si applicano. La superficie di attacco residua è il bundle di asset statici (HTML, CSS, JavaScript) distribuito dalla nostra origine. Progettiamo assumendo:
- Compromissione dell'origine: un attaccante sostituisce uno dei nostri asset inclusi con uno malevolo. Il CSP di seguito limita il raggio d'azione; i token cache-buster rendono facile il rollback a una versione nota come valida.
- Input fornito dall'utente: payload QR, nomi vCard, password WiFi, righe CSV batch, contenuti di immagini scansionate. Ogni percorso di input viene trattato come non attendibile quando viene renderizzato nel DOM.
- Immagini fornite dall'utente: URL di foto vCard e caricamenti di loghi. Le immagini vengono renderizzate in un canvas, mai incorporate nel DOM come markup grezzo.
- Estensioni browser di terze parti: fuori perimetro. Se un'estensione nel tuo browser ha il permesso di modificare ogni pagina, può modificare anche la nostra. Le nostre garanzie valgono quando la pagina viene caricata non modificata.
Content Security Policy, per direttiva
La policy corrente (verifica nelle intestazioni di risposta per qualsiasi richiesta):
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'Cosa permette ogni direttiva e dove scende a compromessi:
default-src 'self', il limite inferiore. Tutto ciò che non allentiamo esplicitamente rimane same-origin.script-src 'self' 'wasm-unsafe-eval', nessun<script>inline, nessuneval().'wasm-unsafe-eval'è richiesto dal percorso di compilazione WebAssembly della nostra libreria encoder QR; NON consente il tradizionaleeval(). Il nostro controllo pre-deploy scansiona ogni pagina HTML alla ricerca di script inline e interrompe la build.style-src 'self' 'unsafe-inline', una vera concessione. La nostra anteprima QR calcola i colori a livello di pixel inline (attributi style sui singoli moduli). Una allowlist basata su hash funzionerebbe ma fallirebbe ad ogni aggiornamento di stile senza un deploy. Compromesso: accettiamo la policy di stile leggermente più debole; lo styling non può comunque esfiltrare dati (CSS non ha portataconnect-src).img-src 'self' data: blob: https:,data:per i render QR inline,blob:per gli URL di download dell'export,https:per gli URL di foto vCard forniti dall'utente. Gli URL forniti dall'utente non vengono mai eseguiti, solo renderizzati.connect-src 'self' https:, fetch() è limitato alla nostra origine più fetch HTTPS avviati dall'utente (es. scansione di un URL foto). Previene l'esfiltrazione via DNS / WebSocket / beacon verso host arbitrari.frame-ancestors 'none', nessun altro sito può incorporarci. Previene il clickjacking.base-uri 'self', un tag<base>iniettato in modo malevolo non può reindirizzare gli URL relativi all'origine di un attaccante.form-action 'self', qualsiasi form iniettata può inviare dati solo alla nostra origine. Non abbiamo form che inviano dati lato server; questo è un approccio belt-and-suspenders.
Policy CSP diverse si applicano a /bio/* (img-src rilassato per avatar forniti dall'utente) e /embed/* (frame-ancestors rilassato per embedding intenzionale). Entrambe sono documentate in site/_headers.
Intestazioni di trasporto e framing
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload, 1 anno, tutti i sottodomini, idoneo per la lista di preload HSTS. I downgrade a HTTP vengono rifiutati dai browser conformi. - X-Frame-Options: DENY, ridondante con CSP
frame-ancestors, mantenuto per la copertura dei browser più datati. - X-Content-Type-Options: nosniff, previene gli attacchi di confusione MIME.
- Referrer-Policy: strict-origin-when-cross-origin, i click sui link in uscita rivelano l'origine ma non il percorso.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=(), la fotocamera è consentita solo per il nostro scanner; microfono e posizione sono esplicitamente negati anche se incorporati.
Service worker
Il nostro service worker (site/sw.js) memorizza nella cache solo gli asset same-origin. Il gestore fetch rifiuta esplicitamente le richieste cross-origin e i metodi non-GET, puoi leggere la logica su GitHub. Le scritture in cache sono avvolte in event.waitUntil() in modo che non possano essere interrotte a metà navigazione.
Sanificazione dell'input
Ogni percorso di rendering che accetta input utente lo tratta come testo non attendibile:
- Le anteprime del payload QR usano
textContent, noninnerHTML. - I target di condivisione (clipboard,
navigator.share()) passano il testo utente come stringa, mai come markup. - Gli export SVG vengono generati dalla nostra libreria encoder; il contenuto utente viene codificato in base64 in
<image>xlink:href, non iniettato come elementi SVG. - Le anteprime di stampa usano un URL blob, non
document.write(). - Il parsing di localStorage è avvolto in
try/catch, una voce corrotta produce un nuovo default vuoto, mai un'eccezione che potrebbe svolgersi in un percorso di codice attivo. - Gli URL forniti dall'utente mostrati come pulsanti "Apri link" sono limitati a
http(s)://, gli schemijavascript:edata:vengono rifiutati.
Recupero di immagini cross-origin
Quando un utente incolla un URL https: come foto vCard o logo, il browser lo recupera soggetto a CORS e alla allowlist img-src del nostro CSP. L'immagine viene renderizzata in un canvas. Non diventa mai DOM attivo, non viene mai eseguita come codice e non raggiunge mai la nostra origine, il fetch è browser → immagine remota, e il risultato viene disegnato lato client. Un attaccante che controlla un URL di immagine remota può tracciare che l'URL è stato caricato (una riga di log sul proprio server) ma non può esfiltrare nulla dalla nostra pagina.
Subresource Integrity (SRI)
Tutto il JavaScript e il CSS che distribuiamo è same-origin. Non carichiamo script o fogli di stile di terze parti, quindi gli hash SRI non sono applicabili. Se mai caricheremo un asset di terze parti, includeremo un attributo SRI integrity su di esso e documenteremo il processo di aggiornamento dell'hash in questa pagina.
Segnalare una vulnerabilità
Se scopri un problema di sicurezza che interessa Abundera QR, nel nostro codice, nel nostro deployment o in una dipendenza che distribuiamo, ti preghiamo di segnalarlo privatamente a security@abundera.ai. Puntiamo a fare triage entro 72 ore. Puoi anche contattarci tramite i dettagli di contatto nel nostro file /.well-known/security.txt.
Nessun bug bounty (ancora)
Al momento non offriamo premi in denaro, ma ogni segnalazione valida confermata riceve credito nel changelog e il nostro ringraziamento pubblico.
Verifica quanto sopra
Ogni affermazione in questa pagina è verificabile dai DevTools del tuo browser senza doversi fidare di noi:
- CSP: DevTools → Network →
/→ Headers. Leggi l'intestazione di rispostaContent-Security-Policy. - HSTS + intestazioni di sicurezza: stesso posto, tutti
Strict-Transport-Security,X-Frame-Options, ecc. sono visibili. - Nessuna chiamata in uscita: DevTools → Network → Fetch/XHR. Genera un QR. Guarda il contatore rimanere a zero.
- Scope del service worker: DevTools → Application → Service Workers. Verifica la sorgente dello script e l'elenco degli asset memorizzati nella cache.
- Nessun cookie: DevTools → Application → Cookies. Vuoto.
- Procedura completa: la sezione di verifica del manifesto.
Contatto
Segnalazioni di sicurezza: security@abundera.ai