Beveiliging
Ons beveiligingsmodel in één zin: er is niets op een server om aan te vallen. Alles hieronder is verifieerbaar vanuit je DevTools.
Architectuur
De generator is een statische single-page applicatie geserveerd vanuit Cloudflare Pages: geen gebruikersaccounts, geen authenticatie en nergens in de generatiestroom een backend-codepad. Elke QR-generatie, -codering, -scan en -renderingoperatie wordt volledig in je browser uitgevoerd. Eén klein serverless endpoint staat ernaast en wordt hier vermeld in plaats van verborgen: /api/contact (het contactformulier, dat wat je indient opslaat in Cloudflare D1 en naar ons e-mailt). Het wordt niet aangeroepen terwijl je een QR-code maakt; de URL-veiligheidscheck is een apart product op check.qr.abundera.ai. Controleer of een QR-code veilig is
Dreigingsmodel
Omdat de generator geen gebruikersdata verzamelt, opslaat of verzendt (de twee endpoints hierboven verwerken alleen wat je er expliciet naartoe stuurt), zijn de meest voorkomende webapp-bedreigingen, diefstal van inloggegevens, databaseinbreuk, sessie-hijacking, server-side injectie, niet op de generator van toepassing. Het resterende aanvalsoppervlak is de statische asset-bundel (HTML, CSS, JavaScript) die vanuit onze origin wordt geserveerd. We ontwerpen met de aanname:
- Origin-compromittering: een aanvaller vervangt een van onze gebundelde assets door een kwaadaardig exemplaar. CSP hieronder beperkt de blastradius; cache-buster tokens maken terugkeer naar een bekende goede versie eenvoudig.
- Door gebruikers ingevoerde invoer: QR-payloads, vCard-namen, wifi-wachtwoorden, batch-CSV-rijen, gescande afbeeldingsinhoud. Elk invoerpad wordt als niet-vertrouwd behandeld wanneer het terug naar de DOM wordt gerenderd.
- Door gebruikers ingevoerde afbeeldingen: vCard-foto-URL's en logo-uploads. Afbeeldingen worden in een canvas gerenderd, nooit als onbewerkte opmaak in de DOM ingesloten.
- Browserextensies van derden: buiten bereik. Als een extensie in je browser toestemming heeft om elke pagina te wijzigen, kan deze ook de onze wijzigen. Onze garanties gelden wanneer de pagina ongewijzigd wordt geladen.
Content Security Policy, per directive
Het onderstaande beleid wordt bij het bouwen van deze pagina gelezen uit het uitgeleverde _headers-bestand, zodat het niet kan afwijken van wat de edge daadwerkelijk verstuurt. Verifieer het in de responsheaders van elk verzoek.
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'Wat elke directive ons toestaat en waar het compromissen biedt:
default-src 'self', de harde vloer. Alles wat we niet expliciet versoepelen, blijft same-origin.script-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com, geen inline<script>, geeneval().abundera.aiis ons eigen bovenliggende domein en serveert één gedeeld script, de taalkiezer.challenges.cloudflare.comis de Turnstile-widget die alleen wordt gebruikt op /contact/.'wasm-unsafe-eval'is vereist door het WebAssembly-compilatiepad van onze QR-encoderbibliotheek; het staat traditioneleeval()NIET toe. Onze eigen pre-deploy-controle scant elke HTML-pagina op inline scripts en laat de build mislukken.style-src 'self' 'unsafe-inline', een echte concessie. Onze QR-preview berekent inline kleuren op pixelniveau (stijlattributen op afzonderlijke modules). Een hash-gebaseerde allowlist zou werken, maar zou mislukken bij elke stijlupdate zonder een deploy. Afweging: we accepteren het iets zwakkere stijlbeleid; styling kan toch geen data exfiltreren (CSS heeft geenconnect-src-bereik).img-src 'self' data: blob: https:,data:voor inline QR-renders,blob:voor export-download-URL's,https:voor door gebruikers opgegeven vCard-foto- en avatar-URL's. Door gebruikers opgegeven URL's worden nooit uitgevoerd, alleen gerenderd. Let op: deze directive is ook een uitgaand kanaal: elk script dat op de pagina draait, zou een afbeelding kunnen opvragen bij een willekeurige HTTPS-host en daarbij data in de URL kunnen stoppen. We accepteren dat omdat de functie het nodig heeft, en we zeggen dat hier liever dan het beleid als waterdicht te omschrijven.connect-src 'self' https: https://challenges.cloudflare.com, degene om aandachtig te lezen.https:is een schema-bron: die laatfetch(), XMLHttpRequest, WebSocket over TLS, EventSource ensendBeacon()elke HTTPS-origin bereiken, niet alleen degene die je hebt getypt. Die is er zodat het vCard-fotoveld en de URL-veiligheidscheck een adres kunnen ophalen dat je hebt geplakt. Het voorkomt geen exfiltratie door een kwaadaardig script; geen enkele CSP-directive doet dat zodra het script al draait als ons. Wat exfiltratie voorkomt, is dat onze uitgeleverde code geen zulke verzoeken doet, wat je kunt zien in het tabblad Netwerk.frame-src 'self' https://challenges.cloudflare.com, alleen de Turnstile-widget mag worden geframed.frame-ancestors 'none', geen andere site kan ons insluiten. Voorkomt clickjacking.base-uri 'self', een kwaadaardig geïnjecteerde<base>-tag kan relatieve URL's niet doorsturen naar de origin van een aanvaller.form-action 'self', elk geïnjecteerd formulier kan alleen terugposten naar onze origin. Het contactformulier is het enige formulier dat ergens naartoe verstuurt.
Wat CSP niet kan
CSP beperkt van waar de browser code laadt. Het controleert niet wat geladen code doet. Als onze origin gecompromitteerd zou zijn, het eerste punt in het dreigingsmodel, zou het script van de aanvaller draaien met dezelfde rechten als de onze, inclusief de connect-src- en img-src-toestemmingen hierboven. Onze verdediging daartegen zit stroomopwaarts van CSP: deploys gaan via één Cloudflare-account met scoped tokens, asset-bestandsnamen zijn content-hashed zodat een vervangen bestand elke URL verandert die ernaar verwijst, en de service worker cachet alleen same-origin GET-responses. Het manifest beschrijft de site zoals die wordt uitgeleverd. Het is geen bewering dat geen enkele webpagina ooit ergens data naartoe zou kunnen sturen, en we willen liever dat je ons aan de nauwere, controleerbare uitspraak houdt.
Verschillende CSP-beleidsregels zijn van toepassing op /bio/* (versoepelde img-src voor door gebruikers opgegeven avatars) en /embed/* (versoepelde frame-ancestors voor opzettelijk insluiten). Beide zijn gedocumenteerd in site/_headers.
Transport- en framing-headers
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload, 1 jaar, alle subdomeinen, in aanmerking komend voor de HSTS-preloadlijst. Downgrades naar HTTP worden geweigerd door conforme browsers. - X-Frame-Options: DENY, redundant met CSP
frame-ancestors, bewaard voor dekking van oudere browsers. - X-Content-Type-Options: nosniff, voorkomt MIME-verwarringsaanvallen.
- Referrer-Policy: strict-origin-when-cross-origin, uitgaande linkklikken lekken de origin maar niet het pad.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=(), camera alleen toegestaan voor onze eigen scanner; microfoon en locatie expliciet geweigerd, ook bij insluiting.
Service worker
Onze service worker (site/sw.js) slaat alleen same-origin-assets in de cache op. De fetch-handler weigert expliciet cross-origin-verzoeken en niet-GET-methoden, je kunt de logica lezen in /sw.js (de broncode wordt zonder sterke minificatie uitgeleverd, genoeg om te volgen). Cache-schrijfbewerkingen zijn gewikkeld in event.waitUntil() zodat ze niet halverwege een navigatie kunnen worden verwijderd.
Invoersanitisatie
Elk renderingpad dat gebruikersinvoer accepteert, behandelt dit als niet-vertrouwde tekst:
- QR-payload-previews gebruiken
textContent, nietinnerHTML. - Share-doelen (klembord,
navigator.share()) geven gebruikerstekst door als een string, nooit als opmaak. - SVG-exports worden gegenereerd vanuit onze encoderbibliotheek; gebruikersinhoud is base64-gecodeerd in
<image>xlink:href, niet geïnjecteerd als SVG-elementen. - Afdrukpreviews gebruiken een blob-URL, niet
document.write(). - localStorage-parsing is gewikkeld in
try/catch, een corrupt item levert een vers leeg standaard op, nooit een uitzondering die in een live codepad kan terechtkomen. - Door gebruikers opgegeven URL's weergegeven als "Link openen"-knoppen zijn beperkt tot
http(s)://,javascript:endata:schema's worden afgewezen.
Cross-origin afbeeldingen ophalen
Wanneer een gebruiker een https:-URL plakt als vCard-foto of logo, haalt de browser deze op via CORS en de img-src-allowlist van onze CSP. De afbeelding wordt in een canvas gerenderd. Het wordt nooit live DOM, wordt nooit uitgevoerd als code en bereikt nooit onze origin, de fetch is browser → externe afbeelding en het resultaat wordt client-side geschilderd. Een aanvaller die een externe afbeeldings-URL beheert, kan bijhouden dat de URL is geladen (een logregel op hun eigen server), maar kan niets van onze pagina exfiltreren.
Subresource Integrity (SRI)
Alle JavaScript en CSS die we leveren is same-origin. We laden geen scripts of stylesheets van derden, dus SRI-hashes zijn niet van toepassing. Als we ooit een asset van een derde partij laden, zullen we een SRI integrity-attribuut eraan toevoegen en het hash-updateproces op deze pagina documenteren.
Een kwetsbaarheid melden
Als je een beveiligingsprobleem ontdekt dat Abundera QR treft, of het nu in onze code, onze implementatie of in een afhankelijkheid die we leveren zit, meld het dan privé aan security@abundera.ai. We streven ernaar binnen 72 uur te triageren. Je kunt ons ook bereiken via de contactgegevens in ons /.well-known/security.txt-bestand.
Nog geen bugbounty
We bieden momenteel geen betaalde bounties aan, maar elk bevestigd geldig rapport ontvangt erkenning in de changelog en onze publieke dankbaarheid.
Verifieer het bovenstaande
Elke bewering op deze pagina is verifieerbaar vanuit de DevTools van je browser zonder ons te vertrouwen:
- CSP: DevTools → Netwerk →
/→ Headers. Lees deContent-Security-Policy-responsheader. - HSTS + beveiligingsheaders: zelfde plaats, alle
Strict-Transport-Security,X-Frame-Options, enz. zijn zichtbaar. - Geen uitgaande oproepen: DevTools → Netwerk → Fetch/XHR. Genereer een QR. Kijk hoe het aantal op nul blijft.
- Service worker-bereik: DevTools → Applicatie → Service Workers. Verifieer de scriptbron en de lijst met gecachede assets.
- Geen cookies: DevTools → Applicatie → Cookies. Leeg.
- Volledige doorloop: de verificatiesectie van het manifest.
Contact
Beveiligingsmededelingen: security@abundera.ai