Jak Reborn Apps chroni Twoje dane - przegląd bezpieczeństwa
Architektura Zero Knowledge, szyfrowanie end-to-end i świadome kompromisy - szczegółowy przegląd tego, jak Reborn Apps chroni Twoje dane.
Reborn Apps to zestaw aplikacji produktywności (zarządzanie zadaniami i notatki) zbudowanych na architekturze True Zero Knowledge end-to-end encryption. W tym wpisie opisujemy model bezpieczeństwa, projekt kryptograficzny i stosowane mechanizmy ochrony.
Architektura Zero Knowledge
Reborn Apps kieruje się ścisłą zasadą Zero Knowledge: serwer nigdy nie ma dostępu do danych użytkownika w postaci jawnej. Całe szyfrowanie i deszyfrowanie odbywa się wyłącznie na urządzeniu użytkownika.
Co serwer zna
| Dane | Widoczność | Uzasadnienie |
|---|---|---|
| Nazwa użytkownika | Jawna | Wymagana do uwierzytelniania i unikalności |
| Hash hasła | Argon2id | Serwer weryfikuje dane logowania; nigdy nie widzi hasła |
| Zaszyfrowany klucz główny | Szyfrogram | Przechowywany dla dostępu między urządzeniami |
| Sól kryptograficzna | Jawna | Wymagana do derywacji klucza po stronie klienta |
| ID rekordów i klucze obce | UUID | Wymagane dla integralności relacyjnej i synchronizacji |
| Znaczniki czasu | Jawne | Wymagane dla synchronizacji delta i rozwiązywania konfliktów |
Czego serwer nigdy nie widzi
- Wszelkie treści użytkownika: tytuły zadań, opisy, treść notatek, nazwy list/folderów/tagów - przechowywane jako pola
*_encrypted(szyfrogram AES-GCM) - Metadane behawioralne: status ukończenia, flagi gwiazdki/przypięcia, terminy, przypomnienia, powiązania tagów - spakowane w jedno pole
metadata_encryptedna rekord - Informacje o urządzeniu: szczegóły sesji są szyfrowane po stronie klienta (
device_info_encrypted) - Żadnych danych PII poza nazwą użytkownika - brak e-maila, telefonu, danych osobowych
Świadome kompromisy prywatności
Serwer widzi graf strukturalny (które zadania należą do której listy, które notatki do którego folderu), ponieważ potrzebuje tego do operacji kaskadowych, synchronizacji i autoryzacji. Jednak:
- Powiązania tagów są w pełni ukryte. Relacje N-do-M ujawniałyby graf korelacji, który mógłby odwzorować zachowania użytkownika. ID tagów są szyfrowane wewnątrz
metadata_encrypted- na serwerze nie istnieje żadna tabela łącząca. - Filtrowanie po tagach działa wyłącznie po stronie klienta, na odszyfrowanym indeksie w pamięci.
Kryptografia
| Cel | Algorytm | Szczegóły |
|---|---|---|
| Haszowanie hasła | Argon2id (hash-wasm) | m=19456, t=3, p=1 |
| Derywacja klucza | PBKDF2 (Web Crypto API) | 600 000 iteracji, SHA-256 |
| Szyfrowanie danych | AES-GCM | Klucze 256-bit, unikalny IV na każdą operację |
| Owijanie klucza głównego | AES-GCM przez klucz PBKDF2 | Klucz główny generowany lokalnie, nigdy transmitowany jawnie |
| Kody odzyskiwania | SHA-256 | 8 jednorazowych kodów (XXXXX-XXXXX), haszowane przed zapisem |
| Podpisywanie tokenów | HMAC-SHA256 (JWT) | Ze wsparciem rotacji sekretu przez podwójną weryfikację |
Cykl życia klucza
- Rejestracja - losowy klucz główny jest generowany na kliencie, owijany kluczem derywowanym z hasła (PBKDF2 600K), a szyfrogram wysyłany na serwer razem z hashem Argon2id.
- Logowanie - serwer zwraca zaszyfrowany klucz główny. Klient derywuje klucz owijający z hasła i odszyfrowuje klucz główny lokalnie.
- Sesja - odszyfrowany klucz główny jest przechowywany w IndexedDB (przetrwa restart przeglądarki/PWA) i automatycznie przywracany przy starcie aplikacji. Klucz jest usuwany wyłącznie przy jawnym wylogowaniu. Jest to zgodne z praktyką branżową (Standard Notes, Bitwarden, Proton Mail) - w architekturze Zero Knowledge ochrona przed lokalnym dostępem do urządzenia jest poza zakresem; ZK chroni przed atakami po stronie serwera.
- Operacje na danych - wszystkie operacje szyfrowania/deszyfrowania używają klucza głównego przez AES-GCM. Klucz nigdy nie opuszcza urządzenia.
Uwierzytelnianie i bezpieczeństwo sesji
Przepływ uwierzytelniania
- Uwierzytelnianie nazwą użytkownika i hasłem z weryfikacją Argon2id po stronie serwera
- Opcjonalne uwierzytelnianie dwuskładnikowe (TOTP)
- Odzyskiwanie konta przez jednorazowe kody odzyskiwania (celowy brak odzyskiwania przez e-mail/telefon)
Zarządzanie tokenami
- Krótkotrwałe tokeny dostępu (JWT) z blacklistą JTI przy wylogowaniu
- Refresh tokeny dostarczane wyłącznie przez ciasteczka
httpOnly- nigdy nie udostępniane w treści odpowiedzi ani dostępne z poziomu JavaScript, co eliminuje kradzież tokenów przez XSS - Rotacja refresh tokenów z śledzeniem rodzin - ponowne użycie refresh tokenu (wskazujące na kradzież) powoduje unieważnienie całej rodziny
- Podwójna obsługa sekretów JWT dla rotacji bez przestoju (
JWT_SECRET+JWT_SECRET_PREVIOUS)
Ochrona przed brute-force
- Blokada po nazwie użytkownika: 5 nieudanych prób uruchamia 15-minutową blokadę, obejmującą logowanie i endpointy 2FA
- Blokada per użytkownik na operacjach wrażliwych: zmiana hasła, wyłączenie 2FA i usunięcie konta wymuszają limity prób z automatyczną blokadą
- 4-warstwowa ochrona anty-bot przy rejestracji:
- Honeypot - ukryte pole odrzucane jeśli wypełnione
- Kontrola czasu - zbyt szybkie zgłoszenia są odrzucane
- Proof-of-Work (PoW) - klient musi rozwiązać wyzwanie HMAC-SHA256 wydane przez serwer (brak zewnętrznego CAPTCHA, zgodne z zasadą Zero Knowledge - brak śledzenia zewnętrznego)
- Weryfikacja sygnatury po stronie serwera - wyzwanie PoW jest podpisane i weryfikowane przed sprawdzeniem rozwiązania
- Odpowiedzi o stałym czasie przy logowaniu i rejestracji - zapobiegają enumeracji nazw użytkowników przez kanały boczne
- Walidacja łańcucha proxy: parsowanie
X-Forwarded-Forod prawej do lewej z konfigurowalnymi zaufanymi proxy (RFC 1918) - ochrona przed IP spoofingiem
Transport i Content Security
Nagłówki bezpieczeństwa HTTP
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains(tylko produkcja) - Content Security Policy: tryb nonce (
mode: 'nonce') - eliminujeunsafe-inlinezscript-src. Źródła skryptów ograniczone do'self','nonce-…'i'wasm-unsafe-eval'(wymagane dla Argon2id WASM). Dodatkowe dyrektywy:base-uri: 'self',form-action: 'self',object-src: 'none' - X-Frame-Options:
DENY- zapobiega clickjackingowi - X-Content-Type-Options:
nosniff- zapobiega sniffingowi typów MIME - Ukrywanie tożsamości serwera:
server_tokens offw nginx - zapobiega ujawnieniu wersji - Limit rozmiaru żądania: maksymalnie 1 MB treści (egzekwowany na poziomie nginx i aplikacji)
Autoryzacja i walidacja danych wejściowych
- Weryfikacja własności na każdym endpoincie danych - wszystkie operacje CRUD filtrują po
user_idz uwierzytelnionego JWT; żaden endpoint nie polega na ID użytkownika dostarczonym przez klienta (IDOR-safe by design, zweryfikowane na wszystkich 47 endpointach API) - Middleware uwierzytelniania w hookach serwerowych obu aplikacji waliduje JWT przy każdym chronionym żądaniu
- Scentralizowana walidacja żądań przez schematy Zod (
validateBody()) na wszystkich endpointach danych (zadania, notatki, foldery, tagi, podzadania, subskrypcje push) - Sanityzacja HTML z renderowania Markdown przez DOMPurify z ograniczonymi schematami URI
Integralność synchronizacji
- Middleware idempotentności na endpointach zapisu zapobiega duplikatom podczas ponownych prób synchronizacji offline
Bezpieczeństwo po stronie klienta
Model przechowywania offline-first
- IndexedDB jest źródłem prawdy - aplikacja działa w pełni offline, synchronizując w tle gdy jest połączenie
- Osobne bazy IndexedDB na aplikację (
Reborn_task_DB,Reborn_notes_DB) - zapobiega korupcji danych między aplikacjami - Brak optymistycznych aktualizacji UI - interfejs odzwierciedla dane dopiero po potwierdzonym zapisie
- Indeksy cieni (odszyfrowane kopie pól sortowania/filtrowania jak
is_completed,due_date) istnieją wyłącznie w lokalnym IndexedDB - są usuwane przed synchronizacją z serwerem
Strażnik szyfrowania (Encryption Guard)
Wszystkie dane opuszczające klienta przechodzą przez 3-warstwowy potok walidacji szyfrowania:
- Post-encrypt: walidacja formatu szyfrogramu (
iv:ciphertext) natychmiast po zaszyfrowaniu - Pre-save: walidacja przed zapisem do IndexedDB
- Pre-sync: walidacja przed wysłaniem na serwer
To podejście obrony w głąb gwarantuje, że dane jawne nie mogą zostać przypadkowo zapisane lub przesłane z powodu błędu szyfrowania.
Cross-app SSO
- Single sign-on między aplikacjami używa współdzielonego
localStoragena tym samym originie (za reverse proxy), nie IndexedDB - zachowując izolację baz danych
Łańcuch dostaw i bezpieczeństwo repozytorium
Build i zależności
- Automatyczne audyty zależności -
pnpm audit(skaner podatności w zainstalowanych bibliotekach) uruchamiany na każdym buildzie CI - Automatyczne aktualizacje zależności przez Renovate - bot otwierający pull requesty z aktualizacjami bibliotek, obejmujące zarówno rutynowe wersje, jak i łatki bezpieczeństwa (funkcja version-updates w Dependabocie jest celowo wyłączona, by nie generować zduplikowanych PR-ów od dwóch botów)
- Granice modułów monorepo egzekwowane przez Nx i ESLint - aplikacje mogą importować wyłącznie z pakietów, które jawnie deklarują jako zależności, co zapobiega przypadkowemu sprzęganiu aplikacji ze sobą
- TypeScript strict mode w całym kodzie
- Hartowanie produkcyjne Docker: kontenery non-root (
su-exec node), brak odsłoniętych portów bazy danych (baza dostępna wyłącznie z wewnętrznej sieci Docker)
Mechanizmy bezpieczeństwa repozytorium GitHub
Ostatnia weryfikacja: 2026-05-12
Poza kodem, który piszemy sami, samo repozytorium na GitHubie jest skonfigurowane tak, że każdy commit i każdy pull request przechodzi przez kilka automatycznych kontroli, zanim będzie można go zmergować. Te zabezpieczenia działają niezależnie od tego, kto otwiera pull request:
- Statyczna analiza kodu (CodeQL) - narzędzie skanujące kod źródłowy pod kątem znanych wzorców podatności. Uruchamiana przy każdym pushu i pull requeście oraz dodatkowo cyklicznie. Wykrycia o powadze High lub wyższej powodują niepowodzenie testu mergowania - pull request nie może zostać zmergowany dopóki problem nie zostanie rozwiązany. Copilot Autofix (asystent AI proponujący poprawki) jest włączony i sugeruje łatki na wykryte problemy.
- Alerty Dependabota o podatnościach - powiadomienia o znanych podatnościach (CVE) dla każdej biblioteki śledzonej przez nasze manifesty, ze skonfigurowanym presetem reguł filtrujących szum alertów
- Alerty Dependabota o malware - osobny kanał dla pakietów oznaczonych jako złośliwe, niezależny od kanału CVE
- Skanowanie sekretów z ochroną push (push protection) - GitHub skanuje każdy commit pod kątem znanych wzorców wyciekniętych poświadczeń (klucze API, tokeny, hasła) i odrzuca push w momencie wysyłki, a nie tylko flaguje po fakcie
- Wykrywanie problemów jakościowych kodu - standardowa analiza jakości GitHuba działa równolegle z CodeQL
- Prywatne zgłaszanie podatności - zewnętrzni badacze mogą zgłaszać znaleziska prywatnym kanałem; zob. SECURITY.md
- Security advisories - kiedy ma to znaczenie, publikowane są zalecenia bezpieczeństwa, by konsumenci downstream mogli zareagować
- Ślad audytowy odrzuconych alertów - znaleziska nie mogą być po cichu zamknięte; odrzucenie wymaga zgłoszenia wniosku, który zostaje w logu audytowym
Znane ograniczenia i transparentność
W duchu transparentności - oto świadome kompromisy w obecnej architekturze:
| Obszar | Status | Uwagi |
|---|---|---|
style-src: 'unsafe-inline' | Zaakceptowany | Wymagany przez generowanie inline stylów SvelteKit. Eksfiltracja przez CSS jest znacznie trudniejsza niż wstrzyknięcie skryptu. |
img-src: 'https:' w Notatkach | Zaakceptowany | Wymagany dla zewnętrznych obrazów osadzonych w notatkach Markdown. Aplikacja zadań ogranicza do 'self' data:. |
| Algorytm JWT | HMAC-SHA256 | Podpisywanie asymetryczne (ES256) planowane, ale jeszcze nie wdrożone. Akceptowalne dla wdrożenia na jednym serwerze. |
| Blacklista tokenów | W pamięci | Blacklista tokenów dostępu nie jest trwała między restartami serwera. Ryzyko niskie dzięki krótkiemu czasowi życia tokenu (15 minut). Trwałość oparta na Redis planowana dla wdrożeń wieloinstancyjnych. |
| Brak odzyskiwania przez e-mail | Celowy | To funkcja, nie ograniczenie - zapewnia, że serwer nie przechowuje żadnych danych PII poza nazwą użytkownika. Użytkownicy są odpowiedzialni za bezpieczne przechowywanie kodów odzyskiwania. |
Zgłaszanie problemów bezpieczeństwa
Jeśli odkryjesz podatność, prosimy o odpowiedzialne zgłoszenie. Szczegóły znajdziesz w pliku SECURITY.md w repozytorium lub otwórz prywatne zgłoszenie bezpieczeństwa przez GitHub.
Ten wpis opisuje stan bezpieczeństwa na kwiecień 2026. Pełna dokumentacja techniczna jest dostępna w repozytorium projektu.
Aktualizacja 2026-05-12 - dodano sekcję o mechanizmach bezpieczeństwa repozytorium GitHub (statyczna analiza CodeQL, alerty Dependabota, skanowanie sekretów z push protection). Te zabezpieczenia były już aktywne w momencie pierwotnej publikacji, ale nie były tutaj opisane.