Bezpieczeństwo
Nasz model bezpieczeństwa w jednym zdaniu: na serwerze nie ma nic do zaatakowania. Wszystko poniżej można zweryfikować z poziomu DevTools.
Architektura
Abundera QR to statyczna aplikacja jednostronicowa serwowana z Cloudflare Pages. Nie ma serwera aplikacji, bazy danych, kont użytkowników, uwierzytelniania, punktów końcowych API ani żadnej ścieżki kodu backendowego przetwarzającego dane użytkownika. Każda operacja generowania, kodowania, skanowania i renderowania kodu QR odbywa się w całości w przeglądarce.
Model zagrożeń
Ponieważ nie zbieramy, nie przechowujemy ani nie przesyłamy żadnych danych użytkownika, najczęstsze zagrożenia aplikacji webowych, kradzież danych logowania, naruszenie bazy danych, przejęcie sesji, wstrzyknięcie po stronie serwera, nie mają zastosowania. Pozostała powierzchnia ataku to statyczny pakiet zasobów (HTML, CSS, JavaScript) serwowany z naszego origin. Projektujemy zakładając:
- Kompromitacja origin: atakujący zastępuje jeden z naszych spakowanych zasobów złośliwym. CSP poniżej ogranicza promień rażenia; tokeny cache-buster ułatwiają przywrócenie do zweryfikowanej dobrej wersji.
- Dane wprowadzone przez użytkownika: ładunki QR, nazwy vCard, hasła WiFi, wiersze CSV, zawartość zeskanowanych obrazów. Każda ścieżka wejściowa jest traktowana jako niezaufana podczas renderowania do DOM.
- Obrazy dostarczane przez użytkownika: adresy URL zdjęć vCard i przesyłane logo. Obrazy są renderowane na kanwasie, nigdy nie są osadzane w DOM jako surowy znacznik.
- Zewnętrzne rozszerzenia przeglądarki: poza zakresem. Jeśli rozszerzenie w przeglądarce ma uprawnienia do modyfikowania każdej strony, może modyfikować też naszą. Nasze gwarancje obowiązują, gdy strona jest załadowana bez modyfikacji.
Content Security Policy, dyrektywa po dyrektywie
Aktualna polityka (sprawdź w nagłówkach odpowiedzi dla dowolnego żądania):
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'Co każda dyrektywa nam umożliwia i gdzie stanowi kompromis:
default-src 'self', twarda podstawa. Wszystko, czego jawnie nie rozluźniamy, pozostaje same-origin.script-src 'self' 'wasm-unsafe-eval', żadnych inline<script>, żadnegoeval().'wasm-unsafe-eval'jest wymagane przez ścieżkę kompilacji WebAssembly naszej biblioteki kodera QR; NIE zezwala na tradycyjnyeval(). Nasz własny pre-deploy check skanuje każdą stronę HTML pod kątem skryptów inline i przerywa build.style-src 'self' 'unsafe-inline', realne ustępstwo. Nasz podgląd QR oblicza inline kolory na poziomie pikseli (atrybuty style na poszczególnych modułach). Allowlist oparta na hashu działałaby, ale zawodziłaby przy każdej aktualizacji stylu bez wdrożenia. Kompromis: akceptujemy nieco słabszą politykę stylów; stylowanie i tak nie może eksfiltrować danych (CSS nie ma zasięguconnect-src).img-src 'self' data: blob: https:,data:dla renderów QR inline,blob:dla URL-i pobierania eksportu,https:dla URL-i zdjęć vCard podanych przez użytkownika. URL-e podane przez użytkownika nigdy nie są wykonywane, tylko renderowane.connect-src 'self' https:, fetch() jest ograniczony do naszego origin oraz inicjowanych przez użytkownika żądań HTTPS (np. skanowanie URL zdjęcia). Zapobiega eksfiltracji przez DNS / WebSocket / beacon do dowolnych hostów.frame-ancestors 'none', żadna inna strona nie może nas osadzić. Zapobiega clickjackingowi.base-uri 'self', złośliwie wstrzyknięty tag<base>nie może przekierować względnych URL-i do origin atakującego.form-action 'self', wstrzyknięty formularz może wysyłać dane tylko do naszego origin. Nie mamy formularzy przesyłających dane po stronie serwera; to dodatkowe zabezpieczenie.
Różne polityki CSP mają zastosowanie do /bio/* (rozluźnione img-src dla awatarów podanych przez użytkownika) i /embed/* (rozluźnione frame-ancestors dla zamierzonego osadzania). Obie są udokumentowane w site/_headers.
Nagłówki transportu i ramkowania
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload, 1 rok, wszystkie subdomeny, kwalifikuje się do listy preload HSTS. Przeglądarki zgodne odmawiają downgrade do HTTP. - X-Frame-Options: DENY, nadmiarowy wobec CSP
frame-ancestors, zachowany dla starszych przeglądarek. - X-Content-Type-Options: nosniff, zapobiega atakom przez pomylenie MIME.
- Referrer-Policy: strict-origin-when-cross-origin, kliknięcia linków wychodzących ujawniają origin, ale nie ścieżkę.
- Permissions-Policy:
camera=(self), microphone=(), geolocation=(), kamera dozwolona tylko dla naszego własnego skanera; mikrofon i lokalizacja jawnie zablokowane nawet po osadzeniu.
Service worker
Nasz service worker (site/sw.js) buforuje tylko zasoby same-origin. Procedura obsługi fetch jawnie odrzuca żądania cross-origin i metody inne niż GET, logikę możesz przeczytać na GitHub. Zapisy do pamięci podręcznej są opakowane w event.waitUntil(), aby nie mogły zostać porzucone w trakcie nawigacji.
Sanityzacja danych wejściowych
Każda ścieżka renderowania akceptująca dane od użytkownika traktuje je jako niezaufany tekst:
- Podglądy ładunku QR używają
textContent, nieinnerHTML. - Cele udostępniania (schowek,
navigator.share()) przekazują tekst użytkownika jako ciąg znaków, nigdy jako znacznik. - Eksporty SVG są generowane przez naszą bibliotekę kodera; treści użytkownika są zakodowane base64 w
<image>xlink:href, nie wstrzykiwane jako elementy SVG. - Podglądy wydruku używają blob URL, nie
document.write(). - Parsowanie localStorage jest opakowane w
try/catch, uszkodzony wpis daje świeże puste ustawienie domyślne, nigdy wyjątek, który mógłby rozwinąć się w aktywną ścieżkę kodu. - URL-e podane przez użytkownika wyświetlane jako przyciski "Otwórz link" są ograniczone do
http(s)://, schematyjavascript:idata:są odrzucane.
Pobieranie obrazów cross-origin
Gdy użytkownik wkleja URL https: jako zdjęcie vCard lub logo, przeglądarka pobiera go z zastrzeżeniem CORS i allowlisty img-src naszego CSP. Obraz jest renderowany na kanwasie. Nigdy nie staje się żywym DOM, nie jest wykonywany jako kod i nie dociera do naszego origin, fetch to przeglądarka → zdalny obraz, a wynik jest malowany po stronie klienta. Atakujący kontrolujący zdalny URL obrazu może śledzić załadowanie tego URL (wpis w logach własnego serwera), ale nie może eksfiltrować niczego z naszej strony.
Subresource Integrity (SRI)
Cały JavaScript i CSS, który dostarczamy, jest same-origin. Nie ładujemy zewnętrznych skryptów ani arkuszy stylów, więc hashe SRI nie mają zastosowania. Jeśli kiedykolwiek załadujemy zewnętrzny zasób, dodamy do niego atrybut integrity SRI i udokumentujemy proces aktualizacji hasha na tej stronie.
Zgłaszanie luki bezpieczeństwa
Jeśli odkryjesz problem z bezpieczeństwem dotyczący Abundera QR, czy to w naszym kodzie, wdrożeniu, czy w zależności, którą dostarczamy, zgłoś go prywatnie na adres security@abundera.ai. Dążymy do triage'u w ciągu 72 godzin. Możesz się z nami skontaktować również przez dane kontaktowe w naszym pliku /.well-known/security.txt.
Brak programu bug bounty (na razie)
Nie oferujemy obecnie płatnych nagród, ale każdy potwierdzony ważny raport otrzymuje uznanie w changelogu i nasze publiczne podziękowania.
Zweryfikuj powyższe
Każde twierdzenie na tej stronie można zweryfikować z poziomu DevTools przeglądarki bez konieczności ufania nam:
- CSP: DevTools → Sieć →
/→ Nagłówki. Odczytaj nagłówek odpowiedziContent-Security-Policy. - HSTS + nagłówki bezpieczeństwa: to samo miejsce, wszystkie
Strict-Transport-Security,X-Frame-Optionsitp. są widoczne. - Brak wychodzących wywołań: DevTools → Sieć → Fetch/XHR. Wygeneruj QR. Obserwuj, jak licznik pozostaje na zero.
- Zakres service workera: DevTools → Aplikacja → Service Workers. Sprawdź źródło skryptu i listę zbuforowanych zasobów.
- Brak ciasteczek: DevTools → Aplikacja → Pliki cookie. Puste.
- Pełny przewodnik: sekcja weryfikacji w manifeście.
Kontakt
Zgłoszenia bezpieczeństwa: security@abundera.ai