verify meldet, dass die Kette neu berechnet wurde
Im Betrieb entdeckt, nicht im Test. Das Staging-Gegenbuch meldete "Der beglaubigte Eintrag 5352 existiert nicht mehr". Rekonstruktion aus dem Protokoll: am 22.08. wurden die Aufbewahrungsfristen auf 0 gesetzt, zwei Cleanups loeschten 3155 Eintraege (id 1-5356), danach lief ein Rehash. Seither meldet die Pruefung "Alle Eintraege sind unveraendert und lueckenlos verkettet". Wahr - und praktisch das Gegenteil dessen, was ein Leser mitnimmt. Der Rehash verknuepft alles neu; die rund 700 Kettenluecken, die davor bestanden, sind seitdem unsichtbar. Nachweisbar im Vorbefund, den der Rehash selbst mitschreibt (R170-01) - nur schaute den nie jemand an. Das Gegenbuch war der einzige Zeuge; innerhalb des CRM war die Loeschung nicht mehr feststellbar. verifyIntegrity sammelt jetzt die Rehash-Marker; die Antwort enthaelt rehashes[] mit Zeitpunkt, Anzahl, Signatur und Vorbefund. Die Meldung nennt sie IMMER, auch im gruenen Fall, und der Einstiegssatz lautet dann "...lueckenlos verkettet - allerdings erst seit der letzten Neuberechnung". valid bleibt unberuehrt. Ein Rehash ist eine legitime Massnahme; ihn dauerhaft als Befund zu fuehren waere der Dauer-Alarm, den wir mit den beglaubigten Luecken gerade beseitigt haben. Melden, nicht alarmieren. Umgekehrte Beweislast als bei Manifest und Siegel: Dort zaehlen nur signierte Traeger, weil ein gefaelschter Marker Luecken wegerklaeren koennte. Hier erzeugt ein Marker eine Warnung - wuerden nur signierte zaehlen, koennte man einen Rehash unsichtbar machen, indem man seine Signatur zerstoert. Deshalb zaehlt jeder auswertbare Marker; eine fehlende Signatur wird zusaetzlich gemeldet. Getestet ueber HTTP gegen eine Wegwerf-DB mit echtem Rehash ueber den regulaeren Endpunkt: sauberer Vorzustand, Loeschung+Rehash (der Staging-Ablauf im Kleinen), und zerstoerte Signatur. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -126,7 +126,12 @@ export default function AuditIntegrityCard() {
|
||||
<div className={`flex items-start gap-4 p-4 rounded-lg border ${stil.box}`}>
|
||||
<AmpelIcon ampel={ampel} />
|
||||
<div className="min-w-0">
|
||||
<p className={`font-medium ${stil.titel}`}>{AMPEL_TITEL[ampel]}</p>
|
||||
<p className={`font-medium ${stil.titel}`}>
|
||||
{AMPEL_TITEL[ampel]}
|
||||
{ampel === 'ok' && ergebnis.rehashes.length > 0 && (
|
||||
<span className="font-normal"> – seit der letzten Neuberechnung</span>
|
||||
)}
|
||||
</p>
|
||||
<p className={`text-sm mt-1 ${stil.text}`}>{ergebnis.message}</p>
|
||||
<p className="text-sm text-gray-500 mt-2">
|
||||
{ergebnis.checkedCount.toLocaleString('de-DE')} Einträge geprüft ·
|
||||
@@ -156,9 +161,36 @@ export default function AuditIntegrityCard() {
|
||||
<Zahl label="Ohne Schlüssel nicht prüfbar" ids={ergebnis.unverifiableEntries} ton="text-gray-700" />
|
||||
{ergebnis.tamperedEntries.length === 0 &&
|
||||
ergebnis.chainGaps.length === 0 &&
|
||||
ergebnis.unverifiableEntries.length === 0 && (
|
||||
ergebnis.unverifiableEntries.length === 0 &&
|
||||
ergebnis.rehashes.length === 0 && (
|
||||
<p className="text-sm text-gray-500 italic">Nichts zu berichten.</p>
|
||||
)}
|
||||
{ergebnis.rehashes.length > 0 && (
|
||||
<div className="pt-2 border-t mt-2">
|
||||
<p className="text-sm font-medium text-gray-700">Neuberechnungen der Kette</p>
|
||||
{ergebnis.rehashes.map((r) => (
|
||||
<p key={r.id} className="text-sm text-gray-600 mt-1">
|
||||
{new Date(r.zeitpunkt).toLocaleString('de-DE')} · Eintrag {r.id}
|
||||
{r.neuBerechnet !== null && ` · ${r.neuBerechnet} Einträge neu verkettet`}
|
||||
{r.vorbefund && (
|
||||
<span className="text-gray-500">
|
||||
{' '}· davor: {r.vorbefund.manipuliert} beanstandet,{' '}
|
||||
{r.vorbefund.luecken} Lücken
|
||||
</span>
|
||||
)}
|
||||
{!r.signiert && (
|
||||
<span className="text-red-700"> · ohne gültige Signatur</span>
|
||||
)}
|
||||
</p>
|
||||
))}
|
||||
<p className="text-xs text-gray-500 mt-2">
|
||||
Ein Rehash verknüpft alle Einträge neu. Was davor als Lücke oder
|
||||
Beanstandung sichtbar war, ist danach nicht mehr in der Kette zu sehen –
|
||||
nur noch im Vorbefund des jeweiligen Eintrags. Die Aussage dieser Prüfung
|
||||
reicht deshalb bis zur letzten Neuberechnung zurück, nicht weiter.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-gray-500 pt-2">
|
||||
<strong>Verändert</strong> heißt: der Inhalt einer bestehenden Zeile passt nicht
|
||||
mehr zu ihrer Prüfsumme – das ist ernst.
|
||||
|
||||
@@ -1749,6 +1749,18 @@ export interface IntegrityResult {
|
||||
unexplainedGaps: number[];
|
||||
/** Alt-Luecken, die das Bestandssiegel als bereits vorhanden beglaubigt. */
|
||||
attestedGaps: number[];
|
||||
/**
|
||||
* Protokollierte Neuberechnungen der Kette. Nach einem Rehash ist die Kette
|
||||
* zwangslaeufig stimmig - auch ueber Loeschungen hinweg. Die Aussage der
|
||||
* Pruefung reicht dann nur bis zur letzten Neuberechnung zurueck.
|
||||
*/
|
||||
rehashes: Array<{
|
||||
id: number;
|
||||
zeitpunkt: string;
|
||||
neuBerechnet: number | null;
|
||||
signiert: boolean;
|
||||
vorbefund: { manipuliert: number; luecken: number } | null;
|
||||
}>;
|
||||
/** Signierte Zeilen, die ohne AUDIT_HMAC_KEY nicht pruefbar sind. */
|
||||
unverifiableEntries: number[];
|
||||
backlogSealStatus: 'kein_siegel' | 'intakt' | 'leer' | 'gebrochen' | 'entfernt' | 'nicht_noetig';
|
||||
|
||||
Reference in New Issue
Block a user