Sécurité
Notre modèle de sécurité en une phrase : il n'y a rien sur un serveur à attaquer. Tout ce qui suit est vérifiable depuis vos DevTools.
Architecture
Abundera QR est une application monopage statique servie depuis Cloudflare Pages. Il n'y a pas de serveur d'application, pas de base de données, pas de comptes utilisateur, pas d'authentification, pas d'endpoints API, et aucun chemin de code backend traitant des données utilisateur. Chaque opération de génération, d'encodage, de scan et de rendu de QR s'exécute entièrement dans votre navigateur.
Modèle de menace
Parce que nous ne collectons, ne stockons ni ne transmettons aucune donnée utilisateur, les menaces les plus courantes des applications web — vol de credentials, violation de base de données, détournement de session, injection côté serveur — ne s'appliquent pas. La surface d'attaque restante est le bundle d'assets statiques (HTML, CSS, JavaScript) servi depuis notre origine. Nous concevons en supposant :
- Compromission de l'origine : un attaquant substitue l'un de nos assets groupés par un asset malveillant. La CSP ci-dessous limite le rayon de l'explosion ; les tokens cache-buster facilitent le retour à une version connue comme valide.
- Entrée fournie par l'utilisateur : payloads QR, noms vCard, mots de passe WiFi, lignes CSV par lot, contenus d'images scannées. Chaque chemin d'entrée est traité comme non fiable lorsqu'il est rendu à nouveau dans le DOM.
- Images fournies par l'utilisateur : URLs de photos vCard et uploads de logos. Les images sont rendues dans un canvas, jamais intégrées dans le DOM comme balisage brut.
- Extensions de navigateur tierces : hors périmètre. Si une extension dans votre navigateur a la permission de modifier chaque page, elle peut modifier la nôtre. Nos garanties tiennent lorsque la page est chargée sans modification.
Politique de Sécurité du Contenu — par directive
La politique actuelle (vérifiez dans les en-têtes de réponse de toute requête) :
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'Ce que chaque directive nous permet de faire et où elle représente un compromis :
default-src 'self'— le plancher dur. Tout ce que nous ne relâchons pas explicitement reste de même origine.script-src 'self' 'wasm-unsafe-eval'— pas de<script>inline, pas deeval().'wasm-unsafe-eval'est nécessaire par le chemin de compilation WebAssembly de notre bibliothèque d'encodage QR ; il ne permet PAS leeval()traditionnel. Notre propre vérification pré-déploiement scanne chaque page HTML à la recherche de scripts inline et fait échouer le build.style-src 'self' 'unsafe-inline'— une vraie concession. Notre aperçu QR calcule des couleurs au niveau pixel inline (attributs de style sur des modules individuels). Une liste d'autorisations basée sur des hash fonctionnerait mais échouerait à toute mise à jour de style sans déploiement. Compromis : nous acceptons la politique de style légèrement plus faible ; les styles ne peuvent pas exfiltrer de données de toute façon (CSS n'a pas de portéeconnect-src).img-src 'self' data: blob: https:—data:pour les rendus QR inline,blob:pour les URLs de téléchargement d'export,https:pour les URLs de photos vCard fournies par l'utilisateur. Les URLs fournies par l'utilisateur ne s'exécutent jamais, elles se rendent seulement.connect-src 'self' https:— fetch() est limité à notre origine plus les requêtes HTTPS initiées par l'utilisateur (ex. : scanner une URL de photo). Empêche l'exfiltration via DNS / WebSocket / beacon vers des hôtes arbitraires.frame-ancestors 'none'— aucun autre site ne peut nous intégrer. Prévient le clickjacking.base-uri 'self'— une balise<base>injectée de façon malveillante ne peut pas rediriger les URLs relatives vers l'origine d'un attaquant.form-action 'self'— tout formulaire injecté ne peut poster qu'à notre origine. Nous n'avons pas de formulaires qui envoient des données côté serveur ; c'est une mesure de sécurité supplémentaire.
Des politiques CSP différentes s'appliquent à /bio/* (img-src assoupli pour les avatars fournis par l'utilisateur) et /embed/* (frame-ancestors assoupli pour l'intégration intentionnelle). Les deux sont documentées dans site/_headers.
En-têtes de transport et de cadrage
- HSTS :
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload— 1 an, tous les sous-domaines, éligible pour la liste de préchargement HSTS. Les rétrogradations vers HTTP sont refusées par les navigateurs conformes. - X-Frame-Options: DENY — redondant avec CSP
frame-ancestors, conservé pour la couverture des navigateurs plus anciens. - X-Content-Type-Options: nosniff — prévient les attaques de confusion MIME.
- Referrer-Policy: strict-origin-when-cross-origin — les clics sur des liens sortants révèlent l'origine mais pas le chemin.
- Permissions-Policy :
camera=(self), microphone=(), geolocation=()— caméra autorisée uniquement pour notre propre scanner ; micro et localisation explicitement refusés même en cas d'intégration.
Service worker
Notre service worker (site/sw.js) met en cache uniquement les assets de même origine. Le gestionnaire fetch rejette explicitement les requêtes cross-origin et les méthodes non-GET — vous pouvez lire la logique sur GitHub. Les écritures en cache sont enveloppées dans event.waitUntil() afin qu'elles ne puissent pas être abandonnées en cours de navigation.
Assainissement des entrées
Chaque chemin de rendu qui accepte une entrée utilisateur la traite comme du texte non fiable :
- Les aperçus de payload QR utilisent
textContent, pasinnerHTML. - Les cibles de partage (presse-papiers,
navigator.share()) transmettent le texte utilisateur comme une chaîne, jamais comme du balisage. - Les exports SVG sont générés par notre bibliothèque d'encodage ; le contenu utilisateur est encodé en base64 dans
<image>xlink:href, pas injecté comme éléments SVG. - Les aperçus d'impression utilisent une URL blob, pas
document.write(). - L'analyse de localStorage est enveloppée dans
try/catch— une entrée corrompue produit une nouvelle valeur par défaut vide, jamais une exception qui pourrait se dérouler dans un chemin de code actif. - Les URLs fournies par l'utilisateur affichées comme boutons "Ouvrir le lien" sont limitées à
http(s)://— les schémasjavascript:etdata:sont rejetés.
Récupération d'images cross-origin
Quand un utilisateur colle une URL https: comme photo vCard ou logo, le navigateur la récupère en respectant CORS et la liste d'autorisation img-src de notre CSP. L'image est rendue dans un canvas. Elle ne devient jamais du DOM actif, ne s'exécute jamais comme du code, et n'atteint jamais notre origine — la récupération se fait navigateur → image distante, et le résultat est rendu côté client. Un attaquant contrôlant une URL d'image distante peut savoir que l'URL a été chargée (une ligne de log sur son propre serveur) mais ne peut pas exfiltrer quoi que ce soit de notre page.
Intégrité des Sous-Ressources (SRI)
Tout le JavaScript et CSS que nous livrons est de même origine. Nous ne chargeons pas de scripts ou feuilles de style tiers, donc les hash SRI ne sont pas applicables. Si jamais nous chargeons un asset tiers, nous lui ajouterons un attribut integrity SRI et documenterons le processus de mise à jour des hash dans cette page.
Signaler une vulnérabilité
Si vous découvrez un problème de sécurité affectant Abundera QR — que ce soit dans notre code, notre déploiement, ou une dépendance que nous livrons — veuillez le signaler de façon privée à security@abundera.ai. Nous visons un triage sous 72 heures. Vous pouvez également nous contacter via les coordonnées de notre fichier /.well-known/security.txt.
Pas de programme de bug bounty (pour l'instant)
Nous n'offrons actuellement pas de primes payées, mais chaque rapport valide confirmé reçoit un crédit dans le changelog et nos remerciements publics.
Vérifiez tout ce qui précède
Chaque affirmation sur cette page est falsifiable depuis les DevTools de votre navigateur sans nous faire confiance :
- CSP : DevTools → Réseau →
/→ En-têtes. Lisez l'en-tête de réponseContent-Security-Policy. - HSTS + en-têtes de sécurité : même endroit — tous
Strict-Transport-Security,X-Frame-Options, etc. sont visibles. - Pas d'appels sortants : DevTools → Réseau → Fetch/XHR. Générez un QR. Observez le compteur rester à zéro.
- Portée du service worker : DevTools → Application → Service Workers. Vérifiez la source du script et la liste des assets en cache.
- Pas de cookies : DevTools → Application → Cookies. Vide.
- Guide complet : la section de vérification du manifeste.
Contact
Divulgations de sécurité : security@abundera.ai