Szczegółowa dokumentacja techniczna kryptograficznych
Transfer plików z szyfrowaniem end-to-end
Dokument techniczny PDF Pro WP-001 — architektura i bezpieczeństwo
Niniejszy dokument techniczny opisuje architekturę systemu Bezpiecznego transferu PDF Pro, mechanizmu transferu plików z szyfrowaniem end-to-end, w którym pliki są szyfrowane po stronie klienta za pomocą AES-256-GCM przed przesłaniem. Serwer przechowuje tylko nieprzezroczysty zaszyfrowany tekst i nie ma możliwości odszyfrowania, sprawdzenia ani odczytania zawartości pliku w żadnym momencie. System obsługuje dwa tryby kluczy: automatycznie wygenerowany losowy klucz (przesyłany przez fragment URL, który nigdy nie jest wysyłany na serwer) lub klucz wyprowadzony z hasła za pomocą PBKDF2. W obu trybach klucz szyfrowania istnieje tylko w przeglądarkach nadawcy i odbiorcy. Niniejszy dokument szczegółowo opisuje prymitywy kryptograficzne, przepływ danych, model zagrożeń i uczciwe ograniczenia systemu. Uwaga dotycząca terminologii: ten system nie używa dowodów wiedzy zerowej (ZKP). Gdy mówimy, że serwer ma "zerową wiedzę" o Twoich plikach, mamy na myśli, że — w wyniku szyfrowania po stronie klienta — serwer zawsze przechowuje tylko zaszyfrowany tekst, którego nie może odszyfrować.
System Bezpiecznego transferu stosuje ścisłą separację klient-serwer, w której wszystkie operacje kryptograficzne odbywają się wyłącznie w przeglądarce:
Serwer jest celowo zaprojektowany jako "głupia rura" dla zaszyfrowanych danych. Nie ma wiedzy o kluczu szyfrowania, haśle ani zawartości pliku. Jest to egzekwowane architektonicznie, a nie tylko przez politykę.
Zasada projektowa: Serwer powinien być możliwy do skompromitowania bez kompromitowania danych użytkownika. Nawet z pełnym dostępem do bazy danych i wykonywaniem kodu po stronie serwera, atakujący nie może odszyfrować przesłanych plików.
Całe szyfrowanie plików wykorzystuje algorytm uwierzytelnionego szyfrowania AES-256-GCM, dostępny przez natywne Web Crypto API przeglądarki. Zapewnia to zarówno poufność (dane nie mogą być odczytane), jak i integralność (dane nie mogą zostać zmodyfikowane bez wykrycia).
| Parametr | Wartość | Uzasadnienie |
|---|---|---|
| Algorytm | AES-256-GCM | Zatwierdzony przez NIST, uwierzytelnione szyfrowanie z powiązanymi danymi (AEAD) |
| Rozmiar klucza | 256 bitów | Maksymalna długość klucza AES; zapewnia 128-bitowe bezpieczeństwo przed atakiem brute force |
| Rozmiar IV | 96 bitów (12 bajtów) | Długość IV zalecana przez NIST dla trybu GCM |
| Rozmiar tagu | 128 bitów (16 bajtów) | Pełna długość tagu uwierzytelniania dla maksymalnej ochrony integralności |
| Generowanie IV | crypto.getRandomValues() | Kryptograficznie bezpieczny generator liczb losowych |
// Uproszczony przepływ szyfrowania (Web Crypto API) const iv = crypto.getRandomValues(new Uint8Array(12)); const salt = crypto.getRandomValues(new Uint8Array(16)); // Wyprowadź klucz z hasła (zobacz sekcję 1.4) const key = await deriveKey(passphrase, salt); // Zaszyfruj plik const ciphertext = await crypto.subtle.encrypt( { name: "AES-GCM", iv: iv }, key, fileArrayBuffer ); // Pakiet: [salt (16B)] + [iv (12B)] + [zaszyfrowany tekst + tag] const blob = concatenate(salt, iv, ciphertext);
Końcowy zaszyfrowany blob jest konkatenacją trzech komponentów: 16-bajtowej soli (używanej do wyprowadzania klucza), 12-bajtowego IV (używanego dla AES-GCM) oraz zaszyfrowanego tekstu z dołączonym 16-bajtowym tagiem uwierzytelniania GCM. Ten blob jest tym, co serwer otrzymuje i przechowuje.
Bezpieczny transfer obsługuje dwa wzajemnie wykluczające się tryby kluczy. Tryb jest wybierany przez nadawcę w momencie tworzenia transferu.
W trybie domyślnym hasło nie jest zaangażowane. Przeglądarka generuje kryptograficznie losowy 256-bitowy klucz AES bezpośrednio:
crypto.getRandomValues() i crypto.subtle.generateKey().#).Gdy nadawca zdecyduje się ustawić hasło, klucz jest wyprowadzany z tego hasła za pomocą PBKDF2:
| Parametr | Wartość | Uzasadnienie |
|---|---|---|
| Algorytm | PBKDF2 | Zalecany przez NIST SP 800-132; szeroko audytowany |
| Funkcja haszująca | SHA-256 | Standardowy hash kryptograficzny; 256-bitowe wyjście |
| Iteracje | 600 000 | Spełnia zalecenie OWASP 2023 dla PBKDF2-SHA256 |
| Rozmiar soli | 128 bitów (16 bajtów) | Unikalna na transfer; zapobiega atakom rainbow table |
| Długość klucza wyjściowego | 256 bitów | Pasuje do wymogu klucza AES-256 |
async function deriveKey(passphrase, salt) { // Importuj hasło jako surowy materiał klucza const keyMaterial = await crypto.subtle.importKey( "raw", new TextEncoder().encode(passphrase), { name: "PBKDF2" }, false, ["deriveKey"] ); // Wyprowadź klucz AES-256-GCM return crypto.subtle.deriveKey( { name: "PBKDF2", salt: salt, iterations: 600000, hash: "SHA-256" }, keyMaterial, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"] ); }
Uwaga dotycząca bezpieczeństwa: Bezpieczeństwo zależy od entropii hasła w trybie B. Zalecamy hasła o długości co najmniej 12 znaków łączące wielkie litery, małe litery, cyfry i symbole.
Pełen cykl życia bezpiecznego transferu przebiega następująco:
crypto.getRandomValues().#).crypto.getRandomValues().| Element danych | Czy serwer ma dostęp? | Szczegóły |
|---|---|---|
| Zaszyfrowany blob (sól + IV + zaszyfrowany tekst + tag) | Tak | Nieprzezroczyste dane binarne; serwer nie może zinterpretować zawartości |
| ID transferu | Tak | Identyfikatory transferu to wartości UUID v4 generowane przez gen_random_uuid() PostgreSQL, zapewniające 122 bity kryptograficznej losowości z CSPRNG serwera |
| Oryginalna nazwa pliku | Tak | Przechowywana w metadanych do wyświetlenia odbiorcy |
| Oryginalny rozmiar pliku | Tak | Przechowywany w metadanych do celów wyświetlania |
| Znacznik czasu wygaśnięcia | Tak | Używany do egzekwowania automatycznego usuwania |
| Liczba pobrań / limit | Tak | Używane do egzekwowania spalania po przeczytaniu i limitu pobrań |
| ID użytkownika nadawcy (jeśli uwierzytelniony) | Tak | Łączy transfer z kontem do zarządzania |
| Hasło | Nie — nigdy | Nigdy nie wysyłane na serwer; nigdy nie opuszcza przeglądarki |
| Wyprowadzony klucz szyfrowania | Nie — nigdy | Istnieje tylko w pamięci przeglądarki podczas szyfrowania/odszyfrowywania |
| Zawartość pliku w postaci jawnej | Nie — nigdy | Tylko zaszyfrowany tekst dociera do serwera |
| Liczba iteracji PBKDF2 | Nie | Zakodowana w kliencie; nie przesyłana na serwer |
Wszystkie bezpieczne transfery są efemeryczne z założenia. Nie ma opcji trwałego przechowywania.
| Opcja | Zachowanie | Egzekwowanie |
|---|---|---|
| Spal po przeczytaniu | Zaszyfrowany blob jest usuwany natychmiast po pierwszym pomyślnym pobraniu | Po stronie serwera: blob usunięty z pamięci po zakończeniu strumienia pobierania |
| 1 godzina | Automatycznie usuwany po 1 godzinie niezależnie od statusu pobierania | Po stronie serwera: zaplanowane zadanie czyszczenia + sprawdzenie wygaśnięcia przy dostępie |
| 24 godziny | Automatycznie usuwany po 24 godzinach | Tak samo jak powyżej |
| 7 dni | Maksymalna retencja; automatycznie usuwany po 7 dniach | Tak samo jak powyżej |
Gdy transfer wygasa lub jest spalany po przeczytaniu, zaszyfrowany blob jest trwale usuwany z Supabase Storage. Rekord metadanych jest zachowywany przez 30 dni w stanie miękkiego usunięcia (do badania nadużyć), zanim zostanie trwale wyczyszczony. Rekord metadanych nie zawiera klucza szyfrowania, hasła ani żadnych informacji, które mogłyby być użyte do zrekonstruowania pliku.
| Zagrożenie | Wektor ataku | Środek łagodzący |
|---|---|---|
| Kompromitacja serwera | Atakujący uzyskuje pełny dostęp do bazy danych i pamięci | Wszystkie przechowywane dane to zaszyfrowany tekst AES-256-GCM. Atakujący uzyskuje tylko nieprzezroczyste bloby. Bez hasła atak brute force na 256-bitowe AES jest obliczeniowo niewykonalny. |
| Przechwycenie sieci (MITM) | Atakujący przechwytuje dane w tranzycie | Cały ruch używa TLS 1.3. Nawet jeśli TLS zostanie złamany, atakujący uzyskuje tylko zaszyfrowane bloby (jak w przypadku kompromitacji serwera). |
| Słabe hasło | Atakujący atakuje brute force krótkie lub powszechne hasło | PBKDF2 z 600 tys. iteracji sprawia, że każda próba jest kosztowna obliczeniowo. 4-znakowe hasło nadal wymagałoby znacznej mocy obliczeniowej. UI egzekwuje minimalną długość hasła i zapewnia informacje zwrotne o sile. |
| Przechwycenie hasła | Atakujący przechwytuje hasło udostępnione między nadawcą a odbiorcą | Hasło jest udostępniane poza pasmem (nie przez nasz system). Zalecamy udostępnianie innym kanałem niż URL transferu. To odpowiedzialność użytkownika. |
| Manipulacja kodem po stronie klienta | Atakujący modyfikuje JavaScript dostarczony użytkownikowi | Wszystkie zasoby dostarczane przez HTTPS z CDN Vercel. Hashe Subresource Integrity (SRI) chronią przed manipulacją na poziomie CDN. Użytkownicy mogą weryfikować kod źródłowy w DevTools przeglądarki. |
| Ekstrakcja pamięci | Atakujący wyodrębnia klucz szyfrowania z pamięci przeglądarki | Klucze Web Crypto API są oznaczane jako nieekstraktowalne tam, gdzie to możliwe. Wyprowadzone klucze istnieją w pamięci tylko podczas operacji szyfrowania/odszyfrowywania. Izolacja pamięci przeglądarki zapewnia ochronę na poziomie systemu operacyjnego. |
| Atak powtórzeniowy | Atakujący odtwarza przechwycony zaszyfrowany blob | Każdy transfer ma unikalne ID i jest chroniony limitami pobrań i wygaśnięciem. Transfery ze spalaniem po przeczytaniu są usuwane po pierwszym dostępie. |
Uczciwe ograniczenia: Żaden system bezpieczeństwa nie jest doskonały. Wierzymy w transparentne ujawnianie znanych ograniczeń.
Kryptograficzne podpisywanie dokumentów stawiające prywatność na pierwszym miejscu
Dokument techniczny PDF Pro WP-002 — architektura i bezpieczeństwo
Niniejszy dokument techniczny opisuje architekturę systemu Podpisu prywatności PDF Pro, kryptograficznego mechanizmu podpisywania dokumentów, w którym dokument PDF nigdy nie opuszcza przeglądarki podpisującego. System wykorzystuje ECDSA z krzywą P-256 i hashowanie SHA-256 do wytwarzania oddzielonych podpisów cyfrowych. Tylko hash dokumentu, podpis kryptograficzny i klucz publiczny są przesyłane na serwer. Klucze prywatne są generowane, szyfrowane i przechowywane wyłącznie w przeglądarce użytkownika za pomocą IndexedDB, chronione przez wyprowadzanie klucza PBKDF2 (600 000 iteracji) i szyfrowanie AES-256-GCM. Niniejszy dokument szczegółowo opisuje pełną architekturę podpisywania i weryfikacji, model zarządzania kluczami, schemat podpisanego ładunku, projekt śladu audytu, model zagrożeń i uczciwe ograniczenia.
System Podpisu prywatności jest zaprojektowany wokół fundamentalnego ograniczenia: dokument PDF nigdy nie może być przesłany na serwer. Jest to egzekwowane architektonicznie przez model oddzielonego podpisu.
Główna gwarancja: Serwer nigdy nie widzi, nie otrzymuje ani nie przetwarza dokumentu PDF. Jedynymi danymi związanymi z dokumentem, które serwer kiedykolwiek otrzymuje, jest hash SHA-256 — wartość 256-bitowa o stałym rozmiarze, z której oryginalnego dokumentu nie można zrekonstruować.
| Parametr | Wartość | Uzasadnienie |
|---|---|---|
| Algorytm podpisu | ECDSA (Elliptic Curve Digital Signature Algorithm) | NIST FIPS 186-4; kompaktowe podpisy; silne bezpieczeństwo na bit klucza |
| Krzywa | P-256 (secp256r1 / prime256v1) | Zatwierdzona przez NIST; 128-bitowy poziom bezpieczeństwa; szerokie wsparcie Web Crypto API |
| Funkcja haszująca | SHA-256 | NIST FIPS 180-4; 256-bitowy skrót; odporna na kolizje |
| Rozmiar klucza | 256-bitowy klucz prywatny, 512-bitowy klucz publiczny (nieskompresowany) | Standard dla P-256; równoważny ~3072-bitowemu RSA |
| Rozmiar podpisu | 64 bajty (r: 32 bajty, s: 32 bajty) | Kompaktowy; odpowiedni do przechowywania i transmisji |
| Format podpisu | IEEE P1363 (surowy r || s) | Natywne wyjście Web Crypto API; kodowane base64url do przechowywania |
Kodowanie podpisu: Web Crypto API ECDSA z P-256 produkuje surowy 64-bajtowy podpis składający się z dwóch 32-bajtowych liczb całkowitych (r || s) w formacie big-endian o stałej szerokości. To surowe wyjście jest kodowane base64url do przechowywania. To NIE jest kodowane DER — to format IEEE P1363, który Web Crypto API natywnie produkuje.
// 1. Hashuj dokument PDF (po stronie klienta) const fileBuffer = await file.arrayBuffer(); const hashBuffer = await crypto.subtle.digest("SHA-256", fileBuffer); const hashHex = Array.from(new Uint8Array(hashBuffer)) .map(b => b.toString(16).padStart(2, '0')).join(''); // 2. Podpisz hash kluczem prywatnym (po stronie klienta) const signature = await crypto.subtle.sign( { name: "ECDSA", hash: "SHA-256" }, privateKey, // CryptoKey z IndexedDB (odszyfrowany) hashBuffer ); // 3. Wyślij na serwer: hash + podpis + klucz publiczny (NIE PDF) await submitSignature({ documentHash: hashHex, signature: base64Encode(signature), publicKey: exportedPublicKeyJWK });
| Typ klucza | Kontekst | Cykl życia | Przechowywanie |
|---|---|---|---|
| Efemeryczne | Użytkownicy goście (niezalogowani) | Generowane na sesję; niszczone po zamknięciu karty | Tylko w pamięci (obiekt CryptoKey); nigdy nie utrwalane |
| Trwałe | Uwierzytelnieni użytkownicy (zalogowani) | Generowane raz; utrwalane między sesjami; możliwe do odwołania | IndexedDB (zaszyfrowane PBKDF2 + AES-256-GCM) |
// Generuj parę kluczy ECDSA P-256 (Web Crypto API) const keyPair = await crypto.subtle.generateKey( { name: "ECDSA", namedCurve: "P-256" }, true, // ekstraktowalny (potrzebny do szyfrowania + przechowywania) ["sign", "verify"] ); // Eksportuj klucz publiczny jako JWK do rejestracji na serwerze const publicKeyJWK = await crypto.subtle.exportKey("jwk", keyPair.publicKey); // Eksportuj klucz prywatny jako JWK do zaszyfrowanego przechowywania const privateKeyJWK = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
Format klucza publicznego: Klucze publiczne są eksportowane i przechowywane w formacie JWK (JSON Web Key). Odcisk palca klucza jest obliczany jako SHA-256 kanonicznego JWK zawierającego tylko pola publiczne {crv, kty, x, y} z kluczami posortowanymi alfabetycznie.
Trwałe klucze prywatne nigdy nie są przechowywane w postaci jawnej. Przed zapisaniem w IndexedDB klucz prywatny (eksportowany jako JWK JSON) jest szyfrowany przy użyciu tego samego wzorca co Bezpieczny transfer:
| Parametr | Wartość |
|---|---|
| Wyprowadzanie klucza | PBKDF2-SHA256, 600 000 iteracji |
| Sól | Losowe 16 bajtów (na klucz) |
| Szyfrowanie | AES-256-GCM |
| IV | Losowe 12 bajtów (na szyfrowanie) |
| Wejście | JWK klucza prywatnego (ciąg JSON, kodowanie UTF-8) |
| Wyjście przechowywane w IndexedDB | { salt, iv, ciphertext, publicKeyJWK, keyId, createdAt } |
// Struktura rekordu IndexedDB dla zaszyfrowanego klucza podpisywania { "keyId": "uuid-v4-unikalny-identyfikator", "publicKey": { /* format JWK, niezaszyfrowany */ }, "encryptedPrivateKey": { "salt": "base64-zakodowane-16-bajtów", "iv": "base64-zakodowane-12-bajtów", "ciphertext": "base64-zakodowany-zaszyfrowany-tekst-aes-gcm" }, "algorithm": "ECDSA", "curve": "P-256", "createdAt": "2026-04-16T00:00:00.000Z", "userId": "id-uzytkownika-supabase-auth" }
Odzyskiwanie kluczy: Jeśli użytkownik zapomni hasła do podpisywania, klucza prywatnego nie można odzyskać. Nie mamy hasła, wyprowadzonego klucza ani żadnego mechanizmu obejścia ochrony PBKDF2 + AES-GCM. Użytkownicy powinni eksportować kopie zapasowe kluczy.
PDF Pro używa modelu oddzielonego podpisu, co oznacza, że podpis jest przechowywany oddzielnie od dokumentu. To krytyczny mechanizm prywatności: dokument nigdy nie opuszcza urządzenia podpisującego.
Gwarancja kryptograficzna: SHA-256 to jednokierunkowa funkcja haszująca. Mając tylko hash e3b0c44298fc1c14..., daje silną gwarancję kryptograficzną, w ramach założeń bezpieczeństwa ECDSA i SHA-256, że oryginalnego dokumentu nie można zrekonstruować. Hash nie ujawnia niczego o zawartości, długości ani strukturze dokumentu poza potwierdzeniem tożsamości po ponownym zhashowaniu.
Następujący schemat JSON definiuje pełen rekord podpisu przechowywany na serwerze:
{
"schemaVersion": "1.0",
"signatureId": "uuid-v4",
"documentHash": "sha256-hex-64-znaków",
"hashAlgorithm": "SHA-256",
"signature": "base64-zakodowany-podpis-ecdsa",
"signatureAlgorithm": "ECDSA",
"curve": "P-256",
"publicKey": {
"kty": "EC",
"crv": "P-256",
"x": "base64url-zakodowana-współrzędna-x",
"y": "base64url-zakodowana-współrzędna-y"
},
"signer": {
"identityLevel": "authenticated | self-asserted",
"displayName": "ciąg lub null",
"email": "ciąg lub null",
"userId": "supabase-uid lub null"
},
"timestamp": "ISO-8601-UTC",
"metadata": {
"fileName": "oryginalna-nazwa-pliku.pdf",
"fileSize": 123456,
"pageCount": 12,
"clientVersion": "2.0.0",
"userAgent": "ciąg-user-agent-przeglądarki"
},
"auditChain": {
"previousEventHash": "sha256-poprzedniego-zdarzenia-audytu lub null",
"eventHash": "sha256-tego-rekordu"
}
}Weryfikacja podpisu to dwufazowy proces: kryptograficzna weryfikacja po stronie klienta, po której następuje weryfikacja krzyżowa po stronie serwera.
const isValid = await crypto.subtle.verify( { name: "ECDSA", hash: "SHA-256" }, importedPublicKey, signatureBuffer, hashBuffer );
isValid === true, podpis jest kryptograficznie ważny: dokument nie został zmodyfikowany od podpisania, a podpis został wykonany przez posiadacza odpowiedniego klucza prywatnego.| Wynik | Znaczenie |
|---|---|
| Ważny (uwierzytelniony) | Podpis jest kryptograficznie ważny ORAZ klucz publiczny należy do zarejestrowanego, uwierzytelnionego użytkownika PDF Pro. |
| Ważny (samodzielnie zadeklarowany) | Podpis jest kryptograficznie ważny, ale tożsamość podpisującego jest samodzielnie zadeklarowana (użytkownik gość lub niezweryfikowana nazwa). |
| Nieważny | Weryfikacja kryptograficzna nie powiodła się. Dokument został zmodyfikowany od podpisania lub podpis jest uszkodzony. |
| Odwołany | Podpis był ważny, ale został wyraźnie odwołany przez podpisującego. |
| Nie znaleziono podpisu | Brak zapisu podpisu dla tego hasha dokumentu. |
Każde zdarzenie podpisywania i weryfikacji jest rejestrowane w odpornym na manipulacje śladzie audytu. Zdarzenia są łączone hashami: każde zdarzenie zawiera hash SHA-256 poprzedniego zdarzenia, tworząc łańcuch tylko do dodawania podobny do blockchaina.
| Typ zdarzenia | Wyzwalacz | Zapisane dane |
|---|---|---|
KEY_REGISTERED | Użytkownik rejestruje nowy klucz publiczny | JWK klucza publicznego, ID użytkownika, znacznik czasu |
DOCUMENT_SIGNED | Użytkownik podpisuje dokument | Hash dokumentu, podpis, klucz publiczny, tożsamość podpisującego, znacznik czasu |
SIGNATURE_VERIFIED | Dowolny użytkownik weryfikuje podpis | Hash dokumentu, wynik weryfikacji, informacje o weryfikatorze (jeśli uwierzytelniony), znacznik czasu |
SIGNATURE_REVOKED | Podpisujący odwołuje podpis | ID podpisu, powód odwołania, znacznik czasu |
KEY_REVOKED | Użytkownik odwołuje klucz publiczny | ID klucza publicznego, powód odwołania, znacznik czasu |
// Każde zdarzenie audytu zawiera: { "eventId": "uuid-v4", "eventType": "DOCUMENT_SIGNED", "timestamp": "ISO-8601-UTC", "data": { /* ładunek specyficzny dla zdarzenia */ }, "previousEventHash": "sha256-poprzedniego-zdarzenia-json", "eventHash": "sha256-tego-zdarzenia-json-bez-eventHash" } // Wykrywanie manipulacji: aby zweryfikować łańcuch, oblicz: // SHA-256(JSON.stringify(zdarzenie bez pola eventHash)) // i potwierdź, że pasuje do eventHash. // Następnie potwierdź, że previousEventHash pasuje do eventHash poprzedniego zdarzenia.
Jeśli jakiekolwiek zdarzenie w łańcuchu zostanie zmodyfikowane, wszystkie kolejne ogniwa hashy zostaną przerwane, czyniąc manipulację natychmiast wykrywalną. Zapewnia to silną gwarancję integralności śladu audytu.
Ważne ograniczenie: Łańcuch hashy sprawia, że manipulacje są wykrywalne w ramach zapisanej sekwencji zdarzeń. Jednak administrator bazy danych z bezpośrednim dostępem mógłby teoretycznie usunąć i zrekonstruować łańcuch. Silniejsze gwarancje wymagałyby zewnętrznego znacznika czasu lub atestacji strony trzeciej, co nie jest zaimplementowane w tej wersji.
| Poziom | Wymagania | Właściwości zaufania | Przypadek użycia |
|---|---|---|---|
| Uwierzytelniony | Zalogowany użytkownik PDF Pro z zweryfikowanym e-mailem; trwała para kluczy zarejestrowana na koncie | E-mail zweryfikowany przez Supabase Auth; klucz publiczny powiązany z uwierzytelnionym kontem; ślad audytu połączony z ID użytkownika | Dokumenty biznesowe, umowy, formalne porozumienia |
| Samodzielnie zadeklarowany | Użytkownik gość lub uwierzytelniony użytkownik z samodzielnie wprowadzoną nazwą; efemeryczna lub trwała para kluczy | Integralność kryptograficzna gwarantowana; tożsamość podpisującego jest samodzielnie zadeklarowana i nie jest niezależnie weryfikowana | Szybkie podpisywanie, dokumenty osobiste, nieformalne porozumienia |
Uwaga: W UI produktu "self_asserted" może być wyświetlane jako "Tożsamość samodzielnie zadeklarowana" lub "Podpisywanie gościa". "authenticated" może być wyświetlane jako "Uwierzytelnione konto". Są to etykiety wyświetlania tych samych poziomów tożsamości.
Powiązanie tożsamości: Podpis "uwierzytelniony" oznacza, że klucz publiczny jest zarejestrowany na koncie PDF Pro ze zweryfikowanym e-mailem. NIE oznacza to, że tożsamość świata rzeczywistego podpisującego została zweryfikowana przez dokument tożsamości wydany przez rząd, biometrię ani weryfikację osobistą. Nie wykonujemy sprawdzeń Know Your Customer (KYC).
| Zagrożenie | Wektor ataku | Środek łagodzący |
|---|---|---|
| Atak powtórzeniowy | Atakujący kopiuje ważny podpis i stosuje go do innego dokumentu | Podpis jest powiązany z hashem SHA-256 konkretnego dokumentu. Inny dokument będzie miał inny hash, a weryfikacja ECDSA się nie powiedzie. |
| Fałszerstwo | Atakujący tworzy ważny podpis bez klucza prywatnego | Bezpieczeństwo ECDSA P-256 opiera się na problemie logarytmu dyskretnego krzywych eliptycznych (ECDLP). Sfałszowanie podpisu bez klucza prywatnego jest obliczeniowo niewykonalne (128-bitowy poziom bezpieczeństwa). |
| Podstawienie klucza | Atakujący rejestruje własny klucz publiczny i twierdzi, że podpis został wykonany przez kogoś innego | Uwierzytelnione podpisy wiążą klucz publiczny ze zweryfikowanym e-mailem. Ślad audytu rejestruje, który klucz podpisał który dokument. Zdarzenia rejestracji kluczy są łączone hashami. |
| Zastąpienie dokumentu | Atakujący modyfikuje podpisany PDF i twierdzi, że podpis jest nadal ważny | Każda modyfikacja PDF zmienia jego hash SHA-256. Istniejący podpis nie przejdzie weryfikacji względem nowego hasha. Znalezienie kolizji (innego dokumentu z tym samym hashem) wymaga ~2^128 operacji. |
| Kradzież klucza prywatnego | Atakujący wyodrębnia zaszyfrowany klucz prywatny z IndexedDB | Klucz prywatny jest zaszyfrowany AES-256-GCM, kluczowany przez PBKDF2 (600 tys. iteracji). Bez hasła odszyfrowanie klucza jest obliczeniowo niewykonalne. |
| Kompromitacja serwera | Atakujący uzyskuje pełny dostęp do serwera | Serwer ma tylko klucze publiczne i zapisy podpisów. Klucze prywatne nigdy nie docierają do serwera. Atakujący nie może sfałszować nowych podpisów. Mógłby usunąć lub zmodyfikować istniejące zapisy, ale przerwy łańcucha hashy byłyby wykrywalne. |
| Manipulacja śladem audytu | Atakujący modyfikuje zdarzenia śladu audytu na serwerze | Zdarzenia łączone hashami: modyfikacja jakiegokolwiek zdarzenia przerywa łańcuch od tego punktu. Niezależne narzędzia weryfikacji mogą wykryć przerwy łańcucha. |
Czym podpisy PDF Pro NIE są:
Nasze zobowiązanie: Budujemy bezpieczeństwo przez architekturę, a nie marketing. Te dokumenty techniczne opisują dokładnie, jak działają nasze systemy, w tym ich ograniczenia. Wierzymy, że użytkownicy świadomi bezpieczeństwa zasługują na pełną przejrzystość techniczną. Jeśli masz pytania dotyczące jakiegokolwiek aspektu naszej architektury, skontaktuj się z nami pod info@webdesign9.com.