Gå til hovedinnhold

Integriteten i revisjonskjeden

Hver administrativ handling som endrer tilstand i fremforge-organisasjonen din, som endringer i SSO-konfigurasjonen, overstyringer av push-beskyttelsen, oppdateringer av branchbeskyttelsen, endringer i fakturering og SCIM-klargjøring, registreres i en revisjonslogg. fremforge går et steg lenger enn en vanlig logg: hver oppføring er kryptografisk kjedet til den forrige, og toppen av kjeden WORM-forankres (Write-Once-Read-Many) i T Cloud Public OBS-lagring med Object Lock hvert 2. minutt.

Resultatet: du kan oppdage enhver manipulering av revisjonsloggen, og du kan verifisere det selv uten å stole på fremverks ord.

Denne siden beskriver hva kjeden garanterer, hva den ikke garanterer, og hvordan du utfører verifiseringen fra ende til ende. Den tekniske forpliktelsen står i DPA vedlegg A.7.

Garantien

For hver oppføring i revisjonsloggen som registreres for organisasjonen din, lagrer fremforge:

  • Selve hendelsen (aktør, handling, tidsstempel, felt).

  • En prev_hash, hashen av den forrige oppføringen for organisasjonen din.

  • En hash, SHA-256 over den kanoniske JSON-en for oppføringen.

    Kanoniseringen er eksakt, og rekkefølgen nedenfor er alfabetisk etter nøkkel, ikke rekkefølgen feltene er listet i andre steder på denne siden. En verifikator som hasher dem i en hvilken som helst annen rekkefølge, får en annen digest og konkluderer, feilaktig, med at kjeden er brutt:

    sha256( {"action":…,"actor":…,"created_at_iso":…,"fields_json":…,"prev_hash":…,"tenant_id":…} )

    Ingen mellomrom. prev_hash er den bokstavelige verdien null ved kjedens epoke. created_at_iso er tidsstempelet som YYYY-MM-DDTHH:mm:ss.sssZ. fields_json er nyttelasten som en JSON-streng, innebygd som en streng og ikke som et nestet objekt, slik at den gjengis byte for byte i stedet for å serialiseres på nytt. Den siste detaljen er den som lurer folk: parser du nyttelasten og serialiserer den på nytt, vil ikke digesten din stemme med vår.

Siden hashen for hver oppføring inkluderer den forrige hashen, bryter en endring i en hvilken som helst oppføring kjeden fremover. GET /orgs/{org}/audit/chain returnerer de rå radene, slik at du kan oppdage det selv; GET /orgs/{org}/audit/integrity rapporterer gjennomgangen vi gjorde.

I tillegg skriver en egen forankringsjobb hvert 2. minutt (tenant_id, latest_hash, count, timestamp) til T Cloud Public OBS med Object Lock-oppbevaring på 1095 dager (3 år), i COMPLIANCE-modus. Forankringsobjektet er fysisk umulig å slette innenfor oppbevaringstiden; lagringslaget avviser slettinger fra alle, også fra fremverk. En angriper som på en eller annen måte endret hele kjeden i databasen, ville fortsatt måtte forholde seg til forankringene i OBS, som ikke stemmer overens.

Åpenhet om hull. Intervallet på 2 minutter er målet; vi lover ikke en kontraktsfestet RPO på ≤2 min for forankringen av kjeden. Hvis T Cloud Public OBS ikke kan nås (regionalt vedlikehold, nettverkshendelse), prøver forankringsjobben igjen ved neste tikk og registrerer hullet i kjedens metadata. Det verste observerte vinduet før alarmen for brudd i kjeden går, er 12 minutter (intervall + terskelen ANCHOR_STALE). Hull dokumenteres i neste hendelsesrapport på statussiden og nevnes i DPA §A.7 om revisjonslogging.

Hva dette fanger opp

TrusselFanget opp?
Innholdet i én revisjonsrad endres (aktør, handling, felt)✓, hash_mismatch på den raden
Én rad slettes fra midten av kjeden✓, prev_hash_mismatch på raden etter
Hele kjeden skrives om av en angriper med full skrivetilgang til databasen✓ hvis omskrivingen skjedde for mer enn ~10 min siden (12 minutter i verste fall), stemmer ikke WORM-forankringen
Hele kjeden skrives om + WORM-bøtten manipuleres ogsåObject Lock i lagringslaget avviser fysisk skrivingen til OBS; ville kreve kompromittering på T Cloud Public-siden
En administrativ handling som ikke sender noen revisjonshendelse i det hele tattDette er et hull i dekningen, ikke et brudd i kjeden; se «Hva dette ikke fanger opp»

Hva dette ikke fanger opp

  • Hull i dekningen. Hvis en kodesti endrer tilstand uten å sende en revisjonshendelse, er kjeden taus. Vi dekker rutene som endrer tilstand, i vedlegg A.7; vi gjennomgår rutene hvert kvartal for å bekrefte at dekningen holder.
  • Historikk fra før vedlegg A.7. Revisjonshendelser som ble sendt før forankring av kjeden ble slått på for organisasjonen din, har en pre-chain-epoch-markør og er uttrykkelig IKKE en del av den verifiserbare kjeden.
  • Lesehendelser. Kjeden registrerer bare endringer. En lekkasje av lesetilgang (noen ser data de ikke skulle sett) vil ikke vises som en oppføring i kjeden. Revisjonsloggen registrerer lesingen der ruten sender en hendelse, men dekningen av ruter som bare leser, er en egen garanti, ikke en garanti fra kjeden.

Verifiser kjeden selv

Dette er en lokal ny beregning, ikke en avlesning av en status. fremforge audit-verify laster ned de rå radene fra GET /orgs/{org}/audit/chain, hver med sin hash og prev_hash, og beregner hver digest og hvert ledd på nytt på din maskin. Deretter henter den vår egen vurdering og sammenligner. Avslutningskoden er ditt resultat; uenighet mellom de to rapporteres og får kommandoen til å feile.

Forskjellen betyr mer enn det kan se ut som. Alt ovenfor denne seksjonen beskriver en garanti mot noen som når databasen, men ikke API-et: WORM-forankringen er i Object Lock COMPLIANCE-modus, så en manipulert database kan ikke bringes til å stemme med den. Ingenting av dette begrenser selve API-et, og frem til august 2026 ble krysskontrollen mellom gjennomgangen av kjeden og forankringen utført av den samme parten som rapporterte svaret. Lokal verifisering er det som gjør garantien kontrollerbar for deg, i stedet for noe vi bare påstår.

Forankringen er signert, og det er du som verifiserer signaturen, ikke vi. Hver forankring skrives med en DSSE-konvolutt signert med den samme Ed25519-nøkkelen som SLSA-byggeproveniensen vår, og den offentlige halvdelen er publisert på www.frem.sh/.well-known/slsa-trust-root.json.

fremforge audit-verify henter denne tillitsroten direkte fra www.frem.sh, ikke fra API-et, og det finnes ingen reserveløsning med en nøkkel som API-et leverer; en signatur som sjekkes mot en nøkkel den reviderte parten ga deg, beviser ingenting. Deretter sjekker den at konvoluttens subject-digest er lik sha256(anchor_head), slik at en konvolutt som er kryptografisk gyldig for en annen forankring, avvises i stedet for å godtas.

To ærlige forbehold:

  • Forankringer skrevet før 2026-08-04 er usignerte og rapporteres som NOT CHECKED. Det finnes ingen signering i ettertid: verdien av en forankring er at den ble skrevet på et bestemt tidspunkt under Object Lock, og å skrive om historikken for å legge til en signatur ville motvirke nettopp det.
  • Signering er med vilje ikke fatal på vår side. Hvis signeringen feiler, skrives forankringen likevel; en usignert forankring er langt bedre enn ingen forankring, fordi forankringen er sikkerhetsnettet som avslører manipulering. Vi har en alarm på det (AUDIT_ANCHOR_UNSIGNED, Major), slik at forringelsen er synlig og ikke stille, og audit-verify rapporterer NOT CHECKED i stedet for å late som.

Bruk fremforge-CLI-en:

# Install (one-liner — see /cli/ for manual download and SHA-256 verification)
curl -sSfL https://cli.frem.sh/cli/install.sh | sh

# Configure auth — generate a PAT at https://frem.sh/user/settings/applications
# with the audit:read scope.
export FREMFORGE_TOKEN='<your PAT>'

# Verify the chain for your org
fremforge audit-verify acme --human

Utdataene ser slik ut:

✓ local verification: ok
  (hashes recomputed on this machine — the server was not asked)

  walk_mode:              tail
  rows_walked:            1284
  verified_count:         1280
  pre_chain_epoch_count:  4
  chain_broken_count:     0
  tenant_erased_count:    0
  payload_redacted_count: 0
  last_verified_hash:     a2b3c4...

✓ WORM anchor (OBS Object Lock):
    anchor_head:            f8e9d0...
    chain_tip_at_anchor:    a2b3c4...
    count_at_anchor:        1280
    timestamp:              2026-05-05T18:00:00.000Z
    age_seconds:            120
    agrees_with_local_walk: true
    signature:              VERIFIED (keyid fremforge-slsa-builder-v1)
    against the trust root fetched from www.frem.sh, not from this API —
    so the anchored tip is the tip that was anchored.

  server verdict:         ok
  server agrees with the local walk.

Uten --human skriver CLI-en ut rå JSON for skripting, inkludert begge vurderingene og eventuell uenighet mellom dem:

# Exit 0 only if YOUR walk was clean and ours agreed
fremforge audit-verify acme || echo "verification failed: $?"

Avslutningskodene, alle utledet fra den lokale gjennomgangen:

  • 0: kjeden ble verifisert, og vår vurdering stemte overens
  • 2: delvis: enten finnes det chain-broken-markørrader (vi skrev revisjonsraden, men kjedetransaksjonen feilet, så raden er bevart for synlighetens skyld, og kjeden mangler et ledd der), eller grensen for antall rader avkortet gjennomgangen, så deler av kjeden ble ikke sjekket. Merk at vi rapporterer ok på en avkortet gjennomgang, mens CLI-en ikke gjør det; å bekrefte rader ingen har gått gjennom, er nøyaktig den typen overdrivelse denne kommandoen finnes for å unngå.
  • 3: brutt: den nyberegnede hashen for en rad stemmer ikke med den lagrede hashen, ELLER radens prev_hash stemmer ikke med den løpende kjeden. Utdataene inneholder rad-ID-en first_break og årsaken.
  • 4: forankringen stemmer ikke: toppen som er WORM-forankret, finnes ikke i kjeden din, noe som betyr at den forankrede raden ble slettet eller endret. Merk at dette er en sjekk av inneholdelse: en aktiv organisasjon der de nyeste radene ennå ikke er forankret, er normalt og består.
  • 5: vi var uenige: gjennomgangen din var ren, men vår vurdering var det ikke. Som regel en rad som ble lagt til mellom de to kallene. Kjør på nytt for å bekrefte; hvis det vedvarer, ser vi noe du ikke ser.

Retningen på en uenighet er ikke symmetrisk, og CLI-en sier hvilken den fant. At vår vurdering er verre enn din, skyldes som regel timing. At vår vurdering er bedre enn din, altså at vi sier ok der din egen utregning sier broken, er nettopp tilfellet hele denne mekanismen finnes for å avdekke, og det rapporteres som en hendelse, ikke som en advarsel.

Hva vi gjør internt

Toppen av kjeden forankres hvert 2. minutt i WORM-lagring, og forankringskjeden verifiseres uavhengig på nytt etter en kontinuerlig plan: hver organisasjons forankringskjede gjennomgås fremover fra starten og sjekkes for kontinuitet. Ethvert avvik, eller enhver organisasjon der forankringen blir foreldet (eldre enn ~10 minutter; 12 minutter i verste fall), varsler vakthavende umiddelbart, som en tidlig indikator på at skrivingen av forankringer har stoppet. Virkningen for kunder dekkes av DPA §11A, og hull dokumenteres på statussiden vår etter en hendelse.

Pseudonymisering under GDPR

Når du bruker retten til sletting, blir revisjonshendelsene for organisasjonen din pseudonymisert, ikke slettet: feltet actor blir [erased], og fields_json tilbakestilles til {}. Leddet i kjeden (hashen) beholdes, slik at kjeden fortsatt kan verifiseres; personopplysningene fjernes.

Det betyr at en kjøring av fremforge audit-verify mot en slettet organisasjon vil vise tenant_erased_count > 0, og disse radene rapporteres separat i stedet for som brudd: en pseudonymisert rad stemmer med vilje ikke lenger med sin egen hash, mens leddet til den neste raden fortsatt sjekkes. De fem tellerne (verified_count, pre_chain_epoch_count, chain_broken_count, tenant_erased_count, payload_redacted_count) summerer ikke til rows_walked: én rad kan være både slettet og ha redigert nyttelast, og telles da under begge. Kjeden er intakt; innholdet er fjernet. Selve radene slettes ikke når oppbevaringsvinduet på 3 år for WORM-forankringen utløper; ingen jobb sletter dem i dag (rettet 2026-10-08: denne siden sa tidligere at de ble slettet).

Uavhengig verifisering

Revisorer kan be om en gjennomgang av kjeden fra ende til ende under NDA; send e-post til compliance@frem.sh for den tekniske vurderingspakken.