Wykrywanie manipulacji w archiwach dokumentów: jak działają łańcuchy skrótów
Samym spojrzeniem nie sposób stwierdzić, czy dokument nie był modyfikowany. Zmieniony plik PDF wygląda identycznie jak oryginał. Podmieniony plik ma tę samą nazwę. Cicho edytowany wpis dziennika audytowego nie pozostawia żadnego widocznego śladu.
Wykrywanie manipulacji wymaga mechanizmu, który sprawia, że modyfikacja powoduje wykrywalny artefakt, najlepiej taki, który może zweryfikować ktoś nieobecny przy pierwotnym tworzeniu dokumentu. Łańcuchy skrótów to jeden z podstawowych mechanizmów pozwalających to osiągnąć.
Co robi kryptograficzny skrót
Kryptograficzna funkcja skrótu przyjmuje dane wejściowe o dowolnej długości i produkuje dane wyjściowe o stałej długości (skrót) z dwiema kluczowymi właściwościami:
- Determinizm: te same dane wejściowe zawsze dają te same dane wyjściowe
- Efekt lawinowy: dowolna zmiana danych wejściowych, nawet o jeden bit, generuje zupełnie inne dane wyjściowe
SHA-256 produkuje 256-bitowy (32-bajtowy) skrót. Dla każdego praktycznego wejścia jest obliczeniowo niemożliwe:
- Znalezienie dwóch różnych danych wejściowych dających to samo wyjście (odporność na kolizje)
- Odtworzenie wejścia z wyjścia (odporność na preobrazy)
- Uzyskanie konkretnego wyjścia bez znajomości wejścia (odporność na drugi preobraz)
Właściwości te oznaczają, że skrót SHA-256 jest wiarygodnym odciskiem palca dokumentu. Jeśli skrót pasuje, dokument jest identyczny z tym, który był skrótem. Jeśli nie pasuje, coś zostało zmienione.
var hash = SHA256.HashData(File.ReadAllBytes("invoice.pdf"));
Console.WriteLine(Convert.ToHexString(hash));
// A3F8C2...1B7D (64 hex chars, always the same for this file, always different if file changes)
Dlaczego pojedynczy skrót nie wystarczy
Obliczenie skrótu dokumentu daje odcisk palca w jednym momencie czasu. Ale kto przechowuje ten odcisk i co stoi na przeszkodzie, by ktoś podmienił zarówno dokument, jak i przechowywany skrót?
Jeśli skrót przechowujesz w tym samym systemie co dokument i to Ty kontrolujesz ten system, zdeterminowany atakujący (lub wewnętrzny pracownik zacierający ślady) może podmienić oba. Skrót nadal będzie pasować do nowego dokumentu. Manipulacja pozostaje niewykryta.
Dlatego wykrywanie manipulacji wymaga zewnętrznej kotwicy: skrótu przechowywanego w miejscu, którego przechowawca nie może jednostronnie modyfikować. Sygnatura czasowa RFC 3161 to standardowy mechanizm: osadza skrót dokumentu wewnątrz tokenu podpisanego przez zewnętrzny TSA. Przechowawca nie może zmienić tokenu bez klucza prywatnego TSA.
Co dodaje łańcuch skrótów
Łańcuch skrótów rozszerza ten mechanizm na sekwencję zdarzeń. Zamiast obliczać skrót tylko dokumentu, oblicza się skrót każdego wpisu audytowego i włącza skrót poprzedniego wpisu do bieżącego.
Hash(Entry N) = SHA256( EventData(N) + Hash(Entry N-1) )
Tworzy to łańcuch, w którym każdy wpis zaświadcza o wszystkich poprzednich. Jeśli zmienisz wpis 3 w łańcuchu złożonym z 10 wpisów, Hash(wpis 3) zmienia się. Ponieważ wpis 4 zawiera Hash(wpis 3), Hash(wpis 4) również się zmienia. I tak dalej aż do wpisu 10.
Nie można cicho modyfikować wcześniejszego wpisu. Łańcuch nie przejdzie walidacji.
Oto minimalna implementacja:
public sealed record AuditEntry(
string EventType,
string Actor,
DateTimeOffset Timestamp,
string DocumentHash,
string PreviousEntryHash)
{
public string ComputeHash()
{
var raw = $"{EventType}|{Actor}|{Timestamp:O}|{DocumentHash}|{PreviousEntryHash}";
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(raw)));
}
}
public static string AppendEntry(IList<AuditEntry> chain, string eventType,
string actor, byte[] documentBytes)
{
var previousHash = chain.Count == 0
? new string('0', 64)
: chain[^1].ComputeHash();
var docHash = Convert.ToHexString(SHA256.HashData(documentBytes));
var entry = new AuditEntry(eventType, actor, DateTimeOffset.UtcNow, docHash, previousHash);
chain.Add(entry);
return entry.ComputeHash();
}
public static bool VerifyChain(IList<AuditEntry> chain)
{
var expectedPrevious = new string('0', 64);
foreach (var entry in chain)
{
if (entry.PreviousEntryHash != expectedPrevious) return false;
expectedPrevious = entry.ComputeHash();
}
return true;
}
Weryfikacja ma złożoność O(n) i nie wymaga zewnętrznych wywołań. Każdy posiadacz łańcucha może uruchomić VerifyChain i potwierdzić, że żaden wpis nie został zmodyfikowany.
Zakotwiczenie łańcucha sygnaturą czasową
Zweryfikowany łańcuch skrótów dowodzi wewnętrznej spójności: żaden wpis nie był modyfikowany po zbudowaniu łańcucha. Nie dowodzi jednak, kiedy łańcuch powstał.
Jeśli dziś zbudujesz fałszywy łańcuch i stwierdzisz, że reprezentuje zdarzenia sprzed trzech lat, sam łańcuch zweryfikuje się poprawnie. Łańcuch skrótów nie ma zegara.
Tutaj sygnatury czasowe RFC 3161 zakotwiczają łańcuch w rzeczywistym czasie. W kluczowych momentach łańcucha (co najmniej przy jego finalizacji, najlepiej przy każdym znaczącym zdarzeniu) należy zażądać sygnatury czasowej od kwalifikowanego TSA. TSA osadza skrót bieżącego wpisu w podpisanym tokenie z zaufaną sygnaturą czasową.
Łańcuch ma teraz zewnętrzną kotwicę. Nie można cofnąć daty łańcucha poza najstarszą sygnaturę czasową, ponieważ token TSA dowodzi, że skrót istniał w danym momencie, a zegar TSA jest autorytatywny.
// After appending an entry, request a timestamp for the entry hash
var entryHash = AppendEntry(chain, "created", "api:tenant-1", documentBytes);
var entryHashBytes = Convert.FromHexString(entryHash);
var timestampToken = await RequestTimestampAsync(entryHashBytes, tsaUrl);
// Store the token alongside the chain entry
chain[^1] = chain[^1] with { TimestampToken = timestampToken };
Audytor weryfikujący łańcuch może:
- Sprawdzić wewnętrzną spójność łańcucha skrótów (brak modyfikacji)
- Zweryfikować każdy token sygnatury czasowej względem publicznego certyfikatu TSA (brak antydatowania)
- Zweryfikować skrót dokumentu w każdym wpisie względem aktualnego dokumentu (brak podstawienia)
Łańcuchy skrótów a inne mechanizmy integralności
Dzienniki tylko do dopisywania (np. tabele bazy danych bez DELETE): Zapobiegają usuwaniu poprzez kontrolę dostępu, ale są tak godne zaufania jak sam system kontroli dostępu. Administrator bazy danych może je obejść. Łańcuchy skrótów są egzekwowane matematycznie, a nie przez politykę.
Podpisy cyfrowe na poszczególnych dokumentach: Podpis dowodzi, że dokument został podpisany konkretnym kluczem w chwili podpisywania. Nie rejestruje, co działo się z dokumentem później. Łańcuch dowodowy wymaga rejestrowania zdarzeń po podpisaniu (przesłanie, konwersja, archiwizacja), a nie tylko samego momentu podpisania.
Blockchain: Używa łańcuchów skrótów jako podstawowego elementu, ale dodaje rozproszony konsensus, by uniemożliwić jednej stronie przepisanie historii. W przypadku archiwów dokumentów lokalny łańcuch skrótów zakotwiczony zewnętrznymi sygnaturami RFC 3161 zapewnia tę samą gwarancję odporności na naruszenia, bez złożoności sieci rozproszonej. Różnica jest istotna: do udowodnienia, że dokument nie był modyfikowany, nie potrzeba konsensusu między obcymi sobie stronami; wymagana jest zewnętrzna kotwica, nad którą nie masz kontroli.
Magazyn WORM (Write Once Read Many): Zapobiega modyfikacji na poziomie warstwy magazynowej. Nośnik fizycznie nie może być nadpisany. Jest to uzupełnienie łańcuchów skrótów, a nie ich zamiennik. WORM dowodzi, że przechowywane bity nie zmieniły się. Łańcuch skrótów dowodzi, jaka sekwencja zdarzeń te bity wygenerowała. Oba razem zapewniają silniejsze gwarancje niż każde z nich osobno.
Jak wygląda weryfikacja w praktyce
Kompletny przepływ weryfikacji dokumentu w archiwum opartym na łańcuchu skrótów:
- Pobierz dokument i ścieżkę audytową z archiwum
- Oblicz SHA-256 dokumentu; potwierdź, że pasuje do
documentHashw ostatnim wpisie łańcucha - Przejdź łańcuch wstecz, ponownie obliczając każdy
entryHashi potwierdzając, że pasuje dopreviousEntryHashw następnym wpisie - Dla każdego wpisu z tokenem sygnatury czasowej zweryfikuj token względem certyfikatu TSA i potwierdź, że skrót w tokenie pasuje do obliczonego
entryHash - Potwierdź, że skrót dokumentu z kroku 2 pasuje do skrótu dokumentu w najwcześniejszym wpisie obejmującym ostateczną postać dokumentu
Jeśli wszystkie pięć kroków zakończy się pomyślnie, dokument jest zweryfikowany jako niezmodyfikowany od momentu jego powstania, ścieżka audytowa dokładnie rejestruje każdy etap przetwarzania, a sygnatury czasowe zapewniają zewnętrznie weryfikowalne kotwice czasowe.
Weryfikacja wymaga: dokumentu, ścieżki audytowej, tokenów sygnatur czasowych oraz publicznych certyfikatów TSA. Bez dostępu do sieci. Bez zależności od działających systemów przechowawcy. Dowody są samowystarczalne.
Wykrywanie manipulacji jako infrastruktura
Większość organizacji nie implementuje łańcuchów skrótów, bo traktuje je jak zaawansowaną funkcjonalność. Tak nie jest. SHA-256 jest dostępny w każdym nowoczesnym środowisku uruchomieniowym. Budowanie łańcucha to kilkadziesiąt linii kodu. Korzyść polega na tym, że można udowodnić integralność dokumentu sceptycznej stronie trzeciej bez polegania na jej zaufaniu do Twojej organizacji.
W regulowanych branżach (finanse, ochrona zdrowia, ubezpieczenia, administracja publiczna) taki dowód nie jest opcjonalny. Dla każdej organizacji chcącej przedstawiać dokumenty cyfrowe jako dowody w sporze jest to poziom bazowy.
SealDoc implementuje ścieżki audytowe oparte na łańcuchu skrótów dla każdego dokumentu w swoim potoku. Łańcuch jest eksportowany w ramach Legal Evidence Pack i jest weryfikowalny offline. Dla organizacji potrzebujących zbudować własną infrastrukturę dowodową SealDoc API udostępnia ścieżkę audytową i tokeny sygnatur czasowych jako dane strukturalne, które można włączyć do własnych przepływów weryfikacji.