← Zpět na všechny články

Hašové řetězce a detekce manipulace: jak jediný změněný bajt zneplatní celý archiv

SealDoc Team · · 7 min čtení

Když někdo řekne, že u archivu dokumentů „lze odhalit manipulaci“, jde o konkrétní tvrzení: jakákoli úprava jakéhokoli záznamu vytvoří zjistitelnou nekonzistenci. Nejde o „odolnost proti manipulaci“: to je jiné a silnější tvrzení. Odhalitelnost manipulace znamená, že pokud byl archiv upraven, dokážete úpravu najít.

Hašové řetězce jsou standardní konstrukcí, jak této vlastnosti dosáhnout. Nejsou nové (koncept je o desetiletí starší než blockchain), ale jsou široce nepochopené, což vede jak k přehnané důvěře, tak k nedostatečnému využívání.

Tento článek vysvětluje, jak hašové řetězce fungují, co zaručují, co nezaručují a proč záleží na konkrétních implementačních volbách.

Co je kryptografický haš

Kryptografická hašovací funkce přijme vstup libovolné délky a vytvoří výstup pevné délky, nazývaný haš nebo digest. SHA-384 vytváří 384bitový (48bajtový) výstup, obvykle zobrazovaný jako 96 hexadecimálních znaků.

Pro detekci manipulace jsou hašovací funkce užitečné díky třem vlastnostem:

Determinismus. Stejný vstup vždy vytvoří stejný výstup. Pokud stejný dokument zahašujete dvakrát, dostanete stejný haš.

Lavinový efekt. Malá změna vstupu vytvoří zcela odlišný výstup. Změna jednoho bajtu v dokumentu o velikosti 10 000 bajtů vytvoří haš, který se původnímu vůbec nepodobá. Nelze nijak předvídat, jak bude nový haš vypadat.

Jednosměrnost. Z daného haše je výpočetně neproveditelné rekonstruovat vstup. Od haše se zpětně k dokumentu nedopracujete.

Právě třetí vlastnost činí hašové řetězce závaznými: jakmile haš zveřejníte nebo zaznamenáte, zavázali jste se k obsahu, z něhož vznikl, aniž byste tento obsah prozradili.

Základní konstrukce

Hašový řetězec propojuje posloupnost událostí tak, že každá událost závisí na všech předchozích událostech.

V implementaci SealDoc obsahuje důkazní událost:

  • Typ události (např. vault.ingested, anchor.rfc3161)
  • Pořadové číslo
  • Časové razítko
  • Podrobnosti specifické pro danou událost (jako kanonický JSON)
  • Haš předchozí události
  • Haš této události, vypočtený ze všech výše uvedených polí

Haš každé události zahrnuje haš předchozí události. To znamená, že k reprodukci haše události N potřebujete přesný obsah události N a přesný haš události N-1. Haš události N-1 závisel na události N-2 a tak dále zpět až k první události.

První událost používá jako svůj „předchozí haš“ pevnou hodnotu genesis: obecně známou konstantu, kterou může ověřit každý.

Tím vzniká řetězec, v němž platí:

Hash(Event 1) = H("vault.ingested" | seq:1 | ts:... | details:... | genesis)
Hash(Event 2) = H("anchor.rfc3161" | seq:2 | ts:... | details:... | Hash(Event 1))
Hash(Event 3) = H("vault.storage-lock.applied" | seq:3 | ts:... | details:... | Hash(Event 2))

Co se stane, když s ním někdo manipuluje

Předpokládejme, že útočník chce upravit podrobnosti události 1. Změní pole v události a poté znovu vypočte haš události 1. Nový haš se od původního liší.

Haš události 2 byl ale vypočten z původního haše události 1. Útočník teď musí znovu vypočítat haš události 2 s použitím nového haše události 1. Tím vznikne pro událost 2 jiný haš.

Teď má událost 3 nesprávný předchozí haš. A tak dále u každé následující události.

Aby úprava zůstala nezjistitelná, musí útočník znovu vypočítat haše všech událostí od místa manipulace dál. V lokálním, uzavřeném systému je to možné. Samotný řetězec tomu nezabrání.

Proto záleží na ukotvení.

Ukotvení: jak učinit řetězec externě ověřitelným

Hašový řetězec, který existuje jen ve vaší vlastní databázi, prokazuje integritu jen v rámci vašeho vlastního systému. Útočník, který ovládá vaši databázi, může přepsat celý řetězec. Řetězec musí být ukotven v něčem, co je mimo vaši kontrolu.

Standardním mechanismem je časové razítko RFC 3161. Postup vypadá takto:

  1. V okamžiku vytvoření dokumentu vypočtěte haš důkazní události.
  2. Odešlete tento haš nezávislé autoritě časového razítka (TSA).
  3. TSA podepíše odpověď obsahující váš haš, časové razítko TSA a identitu TSA. Tato podepsaná odpověď je token časového razítka.
  4. Uložte token časového razítka spolu s hašovým řetězcem.

Token časového razítka je podepsán soukromým klíčem TSA. Řetězec certifikátů TSA je veřejně ověřitelný. Kdokoli, kdo má dokument, původní haš a token časového razítka, může nezávisle ověřit, že dokument v daném stavu v daném čase existoval, aniž by vám musel jakkoli důvěřovat.

Teď má útočník těžší úkol. Aby událost 1 upravil nepozorovaně, musí:

  1. Upravit událost a znovu vypočítat všechny navazující haše.
  2. Získat pro nový haš nové časové razítko RFC 3161 od důvěryhodné TSA.

Druhý krok je zpětně nemožný. Časové razítko TSA má v sobě zabudovaný čas. Časové razítko tvrdící, že upravený dokument existoval v původním čase, by musela TSA vydat právě v tomto původním čase, což útočník dodatečně zařídit nemůže.

Právě to v praxi znamená „odhalitelnost manipulace“: ne že by úpravě bylo zabráněno na fyzické úrovni, ale že každá úprava, kterou chce útočník provést nepozorovaně, vyžaduje buď kompromitaci podpisu nezávislé TSA, nebo přepsání historie, a obojí je výpočetně neproveditelné.

Proč dva digesty a který je kde

SHA-256 je pro tuto třídu aplikací nejčastěji používaným hašovacím algoritmem. SealDoc ho používá pro řetězec zpracování a manifest balíčku a SHA-384 pro důkazní ledger. Oba běží pro každý dokument; odpovídají na různé otázky.

Řetězec zpracování si nemůže volně vybírat. Jeho digest je zároveň klíčem v úložišti a je to otisk zprávy, který podepisuje autorita časového razítka, takže důkazní linie vede od bajtů přes otisk až k tokenu. Změna by znamenala migraci každého uloženého lokátoru a každého existujícího tokenu, nikoli přepnutí nastavení.

Důkazní ledger vznikl později a žádnou takovou vazbu nenese, a proto používá delší digest.

Praktický rozdíl spočívá v rezervě. SHA-256 je v současnosti považován za bezpečný, ale kryptografická doporučení se vyvíjejí. SHA-384 patří do stejné rodiny SHA-2 a počítá se stejně, jen s větším vnitřním stavem. U archivů s dlouhými dobami uchovávání (7 let u faktur s DPH v EU, až 20 let u některých záznamů veřejného sektoru) na dodatečné rezervě záleží.

Delší digest také snižuje (už tak zanedbatelnou) pravděpodobnost kolizí hašů, kdy dva různé dokumenty vytvoří stejný haš. U regulatorních archivů je to podstatná vlastnost: dvě různé faktury by nikdy neměly vytvořit stejný identifikátor.

Ověření řetězce

Ověření probíhá zpětně od nejnovější události. Pro sadu událostí seřazených podle pořadového čísla:

  1. Pro každou událost znovu vypočtěte haš z uložených polí: typu události, pořadového čísla, časového razítka, podrobností a předchozího haše.
  2. Porovnejte znovu vypočtený haš s uloženým hašem. Pokud se shodují, obsah události se od výpočtu haše nezměnil.
  3. Zkontrolujte, že previousHash každé události odpovídá uloženému haši předchozí události. Pokud se shodují, řetězec je nepřerušený.
  4. U každé události, která nese kotvu v podobě časového razítka RFC 3161, ověřte token časového razítka vůči řetězci certifikátů TSA.

Endpoint SealDoc GET /api/vault/{recordId}/verify znovu spočítá každý haš, zkontroluje každé propojení, ověří podpisy událostí a vykáže časovou kotvu, s rozpisem po jednotlivých událostech:

{
  "chainValid": true,
  "eventCount": 3,
  "events": [
    {
      "sequenceNo": 1,
      "eventType": "vault.ingested",
      "hashVerified": true,
      "chainLinkVerified": true,
      "signaturePresent": true,
      "signatureVerified": true
    }
  ]
}

hashVerified znamená, že znovu vypočtený haš odpovídá uloženému haši. chainLinkVerified znamená, že odkaz na předchozí haš je konzistentní. signatureVerified znamená, že podpis ECDSA na haši události je platný vůči známému podpisovému klíči.

Řetězec, v němž každá událost vrací hashVerified: true a chainLinkVerified: true, je řetězec, v němž nebyla upravena žádná uložená data. Kotvící časová razítka poskytují externí, prostřednictvím TSA ověřitelný důkaz o tom, kdy každá událost nastala.

Co řetězec nezaručuje

Odhalitelnost manipulace je konkrétní a ohraničená vlastnost. Vyplatí se přesně vymezit, co pokrývá a co ne.

Nezabraňuje smazání. Hašový řetězec odhalí úpravu existujících záznamů, ale nedokáže odhalit, že byl záznam smazán. Pokud je z řetězce pěti událostí zcela odstraněna událost 2, řetězec se přeruší, ovšem jen tehdy, pokud ověřovatel ví, že událostí má být pět, a ne čtyři. Zabránění smazání je samostatnou záležitostí vrstvy úložiště, například úložiště WORM.

Nezabrání zdrojovému systému lhát. Pokud byl dokument poškozen dřív, než vstoupil do pipeline, hašový řetězec se zaváže k této poškozené verzi. Řetězec prokazuje integritu od okamžiku příjmu dál, nikoli od okamžiku vytvoření. Proto validace (kontrola shody s PDF/A, validace schématu Factur-X) probíhá dřív, než se haš zafixuje.

Řetězec je jen tak silný jako jeho kotvy. Řetězec bez externích kotev RFC 3161 je systém, který pečetí sám sebe. Prokazuje vnitřní konzistenci, ale ne časovou vazbu. Pro jakýkoli seriózní případ použití v oblasti compliance jsou nezbytná časová razítka ukotvená u nezávislé TSA.

Praktický závěr pro inženýry

Pokud budujete pipeline pro dokumenty a potřebujete odhalitelnost manipulace, implementace je přímočará:

  1. Při příjmu vypočtěte haš dokumentu pomocí SHA-256 nebo SHA-384.
  2. Odešlete haš TSA podle RFC 3161 a uložte vrácený token časového razítka.
  3. U každé následující události propojte haš události s hašem předchozí události.
  4. Uložte řetězec do úložiště, které umožňuje jen přidávání (žádné aktualizace ani mazání v tabulce událostí).
  5. Zpřístupněte ověřovací endpoint, který řetězec na vyžádání znovu vypočte a zkontroluje.

Koncepční složitost je nízká. Implementační složitost pramení z provozních záležitostí: správy klíčů, spolehlivosti TSA, migrace řetězce při změně hašovacího algoritmu a ošetření případu, kdy odeslání na TSA selže uprostřed transakce.

SealDoc tyto záležitosti řeší jako infrastrukturu, takže pipeline, které směrují dokumenty přes API, získají řetězec, aniž by ho musely implementovat. Pokud ho ale budujete sami, výše uvedené je minimální životaschopná konstrukce.


SealDoc Evidence Vault vytváří pro každý dokument protokol událostí propojený hašovým řetězcem a vyžaduje časové razítko RFC 3161 od nezávislé autority časového razítka v EU. Řetězec i časová razítka lze ověřit přes GET /api/vault/{recordId}/verify, aniž byste museli důvěřovat infrastruktuře SealDoc.


← Zpět na všechny články