← Terug naar alle artikelen

Hashketens en manipulatiedetectie: hoe één gewijzigde byte een heel archief ongeldig maakt

SealDoc Team · · 9 min lezen

Wie zegt dat een documentarchief “tamper-evident” is, doet een specifieke bewering: elke wijziging van elk record levert een detecteerbare inconsistentie op. Niet tamper-proof: dat is een andere, sterkere bewering. Tamper-evident betekent dat je de wijziging kunt vinden als het archief is gewijzigd.

Hashketens zijn de standaardconstructie om die eigenschap te bereiken. Ze zijn niet nieuw (het concept is tientallen jaren ouder dan blockchain), maar ze worden breed verkeerd begrepen, wat leidt tot zowel overmatig vertrouwen als te weinig gebruik.

Dit artikel legt uit hoe hashketens werken, wat ze garanderen, wat ze niet garanderen en waarom de concrete implementatiekeuzes ertoe doen.

Wat een cryptografische hash is

Een cryptografische hashfunctie neemt een invoer van willekeurige lengte en produceert een uitvoer van vaste lengte, de hash of digest. SHA-384 produceert een uitvoer van 384 bits (48 bytes), meestal weergegeven als 96 hex-tekens.

Drie eigenschappen maken hashfuncties bruikbaar voor manipulatiedetectie:

Determinisme. Dezelfde invoer levert altijd dezelfde uitvoer op. Hash je hetzelfde document twee keer, dan krijg je twee keer dezelfde hash.

Lawine-effect. Een kleine wijziging in de invoer levert een volledig andere uitvoer op. Wijzig je één byte in een document van 10.000 bytes, dan krijg je een hash die in niets op de oorspronkelijke lijkt. Er is geen manier om te voorspellen hoe de nieuwe hash eruit zal zien.

Onomkeerbaarheid. Uit een hash is de invoer computationeel onhaalbaar te reconstrueren. Je kunt niet van de hash terugrekenen naar het document.

De derde eigenschap maakt hashketens bindend: zodra je een hash publiceert of vastlegt, heb je je vastgelegd op de inhoud waaruit hij is ontstaan, zonder die inhoud zelf prijs te geven.

De basisconstructie

Een hashketen koppelt een reeks gebeurtenissen zo dat elke gebeurtenis afhangt van alle voorgaande gebeurtenissen.

In de implementatie van SealDoc bevat een bewijsgebeurtenis:

  • Het type gebeurtenis (bijvoorbeeld vault.ingested, anchor.rfc3161)
  • Een volgnummer
  • Een tijdstempel
  • Details die specifiek zijn voor de gebeurtenis (als canonieke JSON)
  • De hash van de vorige gebeurtenis
  • De hash van deze gebeurtenis, berekend over alle bovenstaande velden

De hash van elke gebeurtenis bevat de hash van de vorige gebeurtenis. Om de hash van gebeurtenis N te reproduceren, heb je dus de exacte inhoud van gebeurtenis N nodig en de exacte hash van gebeurtenis N-1. De hash van gebeurtenis N-1 hing af van gebeurtenis N-2, en zo verder terug tot de eerste gebeurtenis.

De eerste gebeurtenis gebruikt een vaste genesiswaarde als “vorige hash”: een algemeen bekende constante die iedereen kan controleren.

Zo ontstaat een keten waarin:

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))

Wat er gebeurt als je ermee knoeit

Stel dat een aanvaller de details van Event 1 wil wijzigen. Hij past een veld in de gebeurtenis aan en berekent daarna de hash van Event 1 opnieuw. De nieuwe hash verschilt van de oorspronkelijke.

Maar de hash van Event 2 is berekend met de oorspronkelijke hash van Event 1. De aanvaller moet nu de hash van Event 2 opnieuw berekenen met de nieuwe hash van Event 1. Dat levert een andere hash op voor Event 2.

Nu heeft Event 3 de verkeerde vorige hash. En zo verder voor elke volgende gebeurtenis.

Om de wijziging onopgemerkt te laten, moet de aanvaller de hashes van elke gebeurtenis vanaf het gemanipuleerde punt opnieuw berekenen. In een lokaal, op zichzelf staand systeem kan dat. De keten alleen voorkomt dat niet.

Daarom is verankering belangrijk.

Verankering: de keten extern verifieerbaar maken

Een hashketen die alleen in je eigen database bestaat, bewijst integriteit binnen je eigen systeem. Een aanvaller die je database in handen heeft, kan de hele keten herschrijven. De keten moet verankerd zijn aan iets buiten je eigen controle.

Het standaardmechanisme is een RFC 3161-tijdstempel. Dat werkt zo:

  1. Bereken bij het aanmaken van het document de hash van de bewijsgebeurtenis.
  2. Stuur die hash naar een onafhankelijke Time Stamping Authority (TSA).
  3. De TSA ondertekent een antwoord met je hash, de tijdstempel van de TSA en de identiteit van de TSA. Dat ondertekende antwoord is het tijdstempeltoken.
  4. Bewaar het tijdstempeltoken naast de hashketen.

Het tijdstempeltoken is ondertekend met de privésleutel van de TSA. De certificaatketen van de TSA is publiek verifieerbaar. Iedereen met het document, de oorspronkelijke hash en het tijdstempeltoken kan zelfstandig verifiëren dat het document op dat moment in die staat bestond, zonder jou ook maar enigszins te hoeven vertrouwen.

Nu wordt het probleem van de aanvaller lastiger. Om Event 1 onopgemerkt te wijzigen, moet hij:

  1. De gebeurtenis wijzigen en alle hashes daarna opnieuw berekenen.
  2. Een nieuwe RFC 3161-tijdstempel van een vertrouwde TSA voor de nieuwe hash verkrijgen.

De tweede stap is achteraf onmogelijk. In de tijdstempel van de TSA zit een tijd ingebakken. Een tijdstempel die beweert dat het gewijzigde document op het oorspronkelijke tijdstip bestond, zou door de TSA op dat oorspronkelijke tijdstip uitgegeven moeten zijn, en dat kan de aanvaller achteraf niet meer regelen.

Dit is wat “tamper-evident” in de praktijk betekent: niet dat wijziging op fysiek niveau wordt verhinderd, maar dat elke wijziging die een aanvaller onopgemerkt wil laten, vereist dat hij de handtekening van de onafhankelijke TSA compromitteert of de geschiedenis herschrijft. Beide zijn computationeel onhaalbaar.

Waarom twee digests, en welke waar zit

SHA-256 is het meest gebruikte hashalgoritme voor dit soort toepassingen. SealDoc gebruikt het voor de verwerkingsketen en het manifest van het pack, en SHA-384 voor het bewijsledger. Beide draaien voor elk document; ze beantwoorden verschillende vragen.

De verwerkingsketen kan niet vrij kiezen. Haar digest is ook de opslagsleutel, en het is de message imprint die de Time Stamping Authority ondertekent. De bewijslijn loopt dus van de bytes via de imprint naar het token. Wie dat wijzigt, moet elke opgeslagen locator en elk bestaand token migreren; dat is iets anders dan een instelling omzetten.

Het bewijsledger is later gebouwd en kent zo’n binding niet. Daarom gebruikt het de langere digest.

Het praktische verschil is marge. SHA-256 geldt op dit moment als veilig, maar cryptografische aanbevelingen veranderen. SHA-384 hoort tot dezelfde SHA-2-familie en wordt op dezelfde manier berekend, alleen met een grotere interne toestand. Voor archieven met lange bewaartermijnen (7 jaar voor btw-facturen in de EU, tot 20 jaar voor bepaalde overheidsarchieven) doet die extra marge ertoe.

De langere digest verkleint ook de (al verwaarloosbare) kans op hashbotsingen, waarbij twee verschillende documenten dezelfde hash opleveren. Voor compliancearchieven is dat een betekenisvolle eigenschap: twee verschillende facturen mogen nooit dezelfde identificatie opleveren.

De keten verifiëren

Verificatie werkt terug vanaf de laatste gebeurtenis. Gegeven een reeks gebeurtenissen in volgorde van hun volgnummer:

  1. Bereken voor elke gebeurtenis de hash opnieuw uit de opgeslagen velden: type gebeurtenis, volgnummer, tijdstempel, details en vorige hash.
  2. Vergelijk de herberekende hash met de opgeslagen hash. Komen ze overeen, dan is de inhoud van de gebeurtenis niet gewijzigd sinds de hash werd berekend.
  3. Controleer of de previousHash van elke gebeurtenis overeenkomt met de opgeslagen hash van de voorgaande gebeurtenis. Komen ze overeen, dan is de keten ongebroken.
  4. Verifieer voor elke gebeurtenis met een RFC 3161-tijdstempelanker het tijdstempeltoken tegen de certificaatketen van de TSA.

Het endpoint GET /api/vault/{recordId}/verify van SealDoc rekent elke hash opnieuw uit, controleert elke schakel, verifieert de handtekeningen van de gebeurtenissen en rapporteert het tijdanker, met een uitsplitsing per gebeurtenis:

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

hashVerified betekent dat de herberekende hash overeenkomt met de opgeslagen hash. chainLinkVerified betekent dat de verwijzing naar de vorige hash consistent is. signatureVerified betekent dat de ECDSA-handtekening op de hash van de gebeurtenis geldig is tegen de bekende ondertekeningssleutel.

Een keten waarin elke gebeurtenis hashVerified: true en chainLinkVerified: true teruggeeft, is een keten waarin geen opgeslagen gegevens zijn gewijzigd. De ankertijdstempels leveren het externe, via de TSA verifieerbare bewijs van wanneer elke gebeurtenis plaatsvond.

Wat de keten niet garandeert

Tamper-evidence is een specifieke en afgebakende eigenschap. Het loont om precies te zijn over wat die wel en niet dekt.

Ze voorkomt geen verwijdering. Een hashketen detecteert wijziging van bestaande records, maar kan niet detecteren dat een record is verwijderd. Wordt Event 2 in een keten van vijf gebeurtenissen volledig verwijderd, dan breekt de keten, maar alleen als de verificateur weet dat er vijf gebeurtenissen horen te zijn en niet vier. Verwijdering voorkomen is een aparte kwestie voor de opslaglaag, zoals WORM-opslag.

Ze voorkomt niet dat het bronsysteem liegt. Was het document al beschadigd voordat het de pijplijn binnenkwam, dan legt de hashketen zich vast op die beschadigde versie. De keten bewijst integriteit vanaf het moment van inname, niet vanaf het moment van aanmaak. Daarom vindt validatie (PDF/A-conformiteitscontrole, Factur-X-schemavalidatie) plaats voordat de hash wordt vastgelegd.

De keten is maar zo sterk als haar ankers. Een keten zonder externe RFC 3161-ankers is een systeem dat zichzelf verzegelt. Het bewijst interne consistentie, maar geen binding aan de tijd. Voor elke serieuze compliancetoepassing zijn tijdstempels nodig die verankerd zijn bij een onafhankelijke TSA.

Wat dit in de praktijk betekent voor engineers

Bouw je een documentpijplijn en heb je tamper-evidence nodig, dan is de implementatie overzichtelijk:

  1. Bereken bij inname een hash van het document met SHA-256 of SHA-384.
  2. Stuur de hash naar een RFC 3161-TSA en bewaar het tijdstempeltoken dat je terugkrijgt.
  3. Koppel voor elke volgende gebeurtenis de hash van die gebeurtenis aan de hash van de vorige gebeurtenis.
  4. Sla de keten op in append-only-opslag (geen updates of deletes op de tabel met gebeurtenissen).
  5. Bied een verify-endpoint aan dat de keten op verzoek herberekent en controleert.

De conceptuele complexiteit is laag. De complexiteit van de implementatie zit in operationele zaken: sleutelbeheer, betrouwbaarheid van de TSA, migratie van de keten als je het hashalgoritme wijzigt, en de afhandeling van het geval waarin het indienen bij de TSA halverwege een transactie mislukt.

SealDoc regelt deze zaken als infrastructuur: pijplijnen die documenten via de API laten lopen, krijgen de keten zonder hem zelf te implementeren. Bouw je hem zelf, dan is het bovenstaande de minimale werkbare constructie.


De SealDoc Evidence Vault maakt voor elk document een gebeurtenissenlog met hashketen en vraagt een RFC 3161-tijdstempel aan bij een onafhankelijke Time Stamping Authority in de EU. De keten en de tijdstempels zijn verifieerbaar via GET /api/vault/{recordId}/verify, zonder dat je de infrastructuur van SealDoc hoeft te vertrouwen.


← Terug naar alle artikelen