Bezpečnost
Náš bezpečnostní model v jedné větě: na serveru není nic, co by bylo možné napadnout. Vše níže lze ověřit přímo z vašich DevTools.
Architektura
Generátor je statická jednostránková aplikace hostovaná na Cloudflare Pages: žádné uživatelské účty, žádná autentizace a žádná backendová kódová cesta kdekoli v procesu generování. Každá operace generování, kódování, skenování a renderování QR probíhá výhradně ve vašem prohlížeči. Vedle něj běží jeden malý serverless endpoint, který tu raději uvádíme, než abychom ho skrývali: /api/contact (kontaktní formulář, který uloží, co odešlete, do Cloudflare D1 a pošle nám to e-mailem). Nevolá se při vytváření QR kódu; kontrola bezpečnosti URL je samostatný produkt na check.qr.abundera.ai. Zkontrolovat, zda je QR kód bezpečný
Model hrozeb
Protože generátor neshromažďuje, neukládá ani nepřenáší žádná uživatelská data (oba výše uvedené endpointy zpracovávají pouze to, co jim výslovně odešlete), nejběžnější hrozby webových aplikací — krádež přihlašovacích údajů, únik databáze, únos relace, server-side injekce — se na něj nevztahují. Zbývající útočná plocha je statický balík prostředků (HTML, CSS, JavaScript) podávaný z našeho originu. Navrhujeme s předpokladem:
- Kompromitace originu: útočník nahradí jeden z našich zabalených prostředků škodlivým. Níže uvedená CSP omezuje dosah škod; cache-buster tokeny usnadňují návrat ke známé správné verzi.
- Uživatelský vstup: QR payloady, jména vCard, hesla WiFi, řádky dávkového CSV, obsah naskenovaných obrázků. Každá vstupní cesta je považována za nedůvěryhodnou při renderování zpět do DOM.
- Uživatelské obrázky: URL adresy fotek vCard a nahrávání log. Obrázky jsou renderovány do canvasu, nikdy vloženy do DOM jako surové markup.
- Rozšíření prohlížeče třetích stran: mimo rozsah. Pokud má rozšíření ve vašem prohlížeči oprávnění upravovat každou stránku, může upravit i tu naši. Naše záruky platí při načtení stránky v neupravené podobě.
Content Security Policy — podle direktiv
Politika níže se načítá z nasazeného souboru _headers při sestavení této stránky, takže se nemůže odchýlit od toho, co posílá edge. Ověřte ji v hlavičkách odpovědi jakéhokoli požadavku.
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'Co nám každá direktiva umožňuje a kde dělá kompromisy:
default-src 'self'— pevné dno. Vše, co explicitně neuvolníme, zůstane same-origin.script-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com— žádný inline<script>, žádnýeval().abundera.aije naše vlastní nadřazená doména a poskytuje jeden sdílený skript, přepínač jazyka.challenges.cloudflare.comje widget Turnstile použitý pouze na /contact/.'wasm-unsafe-eval'je potřebný pro cestu kompilace WebAssembly naší knihovny QR enkodéru; NEPOVOLUJE tradičníeval(). Naše pre-deploy kontrola prohledá každou HTML stránku na inline skripty a selže sestavení.style-src 'self' 'unsafe-inline'— skutečná ústupek. Náš náhled QR vypočítává barvy na úrovni pixelů inline (atributy style na jednotlivých modulech). Seznam povolených na základě hashů by fungoval, ale selhal by při jakékoli aktualizaci stylu bez nasazení. Kompromis: přijímáme o něco slabší politiku stylování; styly nemohou stejně exfiltrovat data (CSS nemá dosahconnect-src).img-src 'self' data: blob: https:—data:pro inline renderování QR,blob:pro URL pro stahování exportů,https:pro URL fotek a avatarů vCard zadané uživatelem. URL zadané uživatelem se nikdy nespouštějí, pouze renderují. Mějte na paměti, že tato direktiva je zároveň odchozím kanálem: jakýkoli skript běžící na stránce by mohl požádat o obrázek z libovolného HTTPS hostitele a vložit data do URL. Přijímáme to, protože to funkce potřebuje, a raději to zde říkáme, než abychom politiku popisovali jako neprůstřelnou.connect-src 'self' https: https://challenges.cloudflare.com— ta, kterou je třeba číst pozorně.https:je zdroj podle schématu: umožňujefetch(), XMLHttpRequest, WebSocket přes TLS, EventSource asendBeacon()dosáhnout libovolného HTTPS originu, ne jen těch, které jste zadali. Je tam proto, aby pole fotky vCard a kontrola bezpečnosti URL mohly načíst adresu, kterou jste vložili. Nezabraňuje exfiltraci škodlivým skriptem; to nedokáže žádná direktiva CSP, jakmile skript už běží jako my. Exfiltraci brání to, že náš dodávaný kód žádné takové požadavky neprovádí, což si můžete ověřit na záložce Síť.frame-src 'self' https://challenges.cloudflare.com— vložit lze pouze widget Turnstile.frame-ancestors 'none'— žádná jiná stránka nás nemůže vložit. Zabraňuje clickjackingu.base-uri 'self'— injektovaný škodlivý tag<base>nemůže přesměrovat relativní URL na origin útočníka.form-action 'self'— injektovaný formulář může odesílat pouze zpět na náš origin. Jediný formulář, který kamkoli odesílá data, je kontaktní formulář.
Co CSP nedokáže
CSP omezuje, odkud prohlížeč načte kód. Nekontroluje, co načtený kód dělá. Kdyby byl náš origin kompromitován, první položka modelu hrozeb, skript útočníka by běžel se stejnými oprávněními jako náš, včetně výše uvedených povolení connect-src a img-src. Naše obrana proti tomu stojí před CSP: nasazení probíhají přes jeden účet Cloudflare se zúženými tokeny, názvy souborů prostředků jsou hashovány podle obsahu, takže vyměněný soubor změní každou URL, která na něj odkazuje, a service worker ukládá do cache pouze same-origin odpovědi GET. Manifest popisuje web tak, jak je dodáván. Není to tvrzení, že žádná webová stránka nikdy nemůže nikam poslat data, a raději bychom, abyste nás drželi u tohoto užšího, ověřitelného tvrzení.
Různé CSP politiky se vztahují na /bio/* (uvolněný img-src pro avatary zadané uživatelem) a /embed/* (uvolněný frame-ancestors pro záměrné vkládání). Obě jsou zdokumentovány v site/_headers.
Transportní a rámovací hlavičky
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload— 1 rok, všechny subdomény, způsobilé pro seznam předběžného načítání HSTS. Downgrade na HTTP odmítají kompatibilní prohlížeče. - X-Frame-Options: DENY — redundantní s CSP
frame-ancestors, zachováno pro podporu starších prohlížečů. - X-Content-Type-Options: nosniff — zabraňuje útokům zmatení MIME.
- Referrer-Policy: strict-origin-when-cross-origin — kliknutí na odchozí odkaz prozradí origin, nikoli cestu.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=()— kamera povolena pouze pro náš vlastní skener; mikrofon a poloha explicitně odepřeny i při vložení.
Service worker
Náš service worker (site/sw.js) ukládá do cache pouze same-origin prostředky. Obslužná rutina fetch explicitně odmítá cross-origin požadavky a metody jiné než GET — logiku si můžete přečíst v souboru /sw.js (zdroj je dodáván dostatečně neminifikovaný, aby šel sledovat). Zápisy do cache jsou zabaleny v event.waitUntil(), takže nemohou být zahozeny uprostřed navigace.
Sanitace vstupů
Každá renderovací cesta přijímající uživatelský vstup s ním zachází jako s nedůvěryhodným textem:
- Náhledy QR payloadů používají
textContent, nikoliinnerHTML. - Cíle sdílení (schránka,
navigator.share()) předávají uživatelský text jako řetězec, nikdy jako markup. - SVG exporty jsou generovány z naší knihovny enkodéru; uživatelský obsah je base64 zakódován do
<image>xlink:href, nevkládán jako SVG elementy. - Náhledy tisku používají blob URL, nikoli
document.write(). - Parsování localStorage je zabaleno v
try/catch— poškozená položka vrátí novou prázdnou výchozí hodnotu, nikdy výjimku, která by se mohla prosadit do živé kódové cesty. - URL zadané uživatelem zobrazené jako tlačítka "Otevřít odkaz" jsou omezeny na
http(s)://— schématajavascript:adata:jsou odmítnuta.
Načítání obrázků z jiného originu
Když uživatel vloží URL https: jako foto vCard nebo logo, prohlížeč ji načte s ohledem na CORS a seznam povolených img-src v naší CSP. Obrázek se renderuje do canvasu. Nikdy se nestane živým DOM, nikdy nespustí jako kód a nikdy nedosáhne našeho originu — načtení probíhá prohlížeč → vzdálený obrázek a výsledek je vykreslen na straně klienta. Útočník kontrolující vzdálenou URL obrázku může sledovat, že URL byla načtena (řádek záznamu na jeho vlastním serveru), ale nemůže z naší stránky nic exfiltrovat.
Subresource Integrity (SRI)
Veškerý JavaScript a CSS, který dodáváme, je same-origin. Nenačítáme skripty ani styly třetích stran, takže SRI hashe nejsou relevantní. Pokud kdy načteme prostředek třetí strany, přibalíme k němu atribut SRI integrity a zdokumentujeme proces aktualizace hashe na této stránce.
Nahlášení zranitelnosti
Pokud odhalíte bezpečnostní problém ovlivňující Abundera QR — v našem kódu, nasazení nebo závislosti, kterou dodáváme — prosím nahlaste jej soukromě na security@abundera.ai. Snažíme se provést třídění do 72 hodin. Můžete nás také kontaktovat prostřednictvím kontaktních údajů v souboru /.well-known/security.txt.
Zatím žádný bug bounty
V současnosti nenabízíme placené odměny, ale každá potvrzená platná zpráva dostane uznání v changelogu a naše veřejné poděkování.
Ověřte cokoli z výše uvedeného
Každé tvrzení na této stránce lze vyvrátit z DevTools vašeho prohlížeče bez toho, abyste nám museli důvěřovat:
- CSP: DevTools → Síť →
/→ Hlavičky. Přečtěte hlavičku odpovědiContent-Security-Policy. - HSTS + bezpečnostní hlavičky: na stejném místě — všechny
Strict-Transport-Security,X-Frame-Optionsatd. jsou viditelné. - Žádná odchozí volání: DevTools → Síť → Fetch/XHR. Vygenerujte QR. Sledujte, jak počet zůstává na nule.
- Rozsah service workeru: DevTools → Aplikace → Service Workers. Ověřte zdroj skriptu a seznam uložených prostředků.
- Žádné cookies: DevTools → Aplikace → Cookies. Prázdné.
- Úplný průvodce: sekce ověření v manifestu.
Kontakt
Bezpečnostní oznámení: security@abundera.ai