Research project — not intended for production use.
GhostAudit hides audit logs steganographically inside ordinary SQLite user data. In the current V9 path, the carrier is a real application table supplied by the caller, and GhostAudit overlays bits on normal app writes. A privileged attacker who deletes the visible log tables finds nothing — the real forensic data is invisibly embedded in carrier fields and cryptographically secured.
from core.carrier_config import CarrierConfig
from core.ghost_audit_v9 import GhostAuditInterceptor
import secrets
carrier = CarrierConfig(
table="users",
id_field="id",
semantic_field="bio",
float_a_field="trust_score",
float_b_field="profile_score",
tilde_field="avatar_url",
timestamp_field="created_at", # V9.3: Ch2 Timestamp-LSB (kein Bio-SPOF)
integer_channel_field="ui_prefs", # V9.9: Ch0 Integer-LSB (kein Synonym-Fingerabdruck)
)
ga = GhostAuditInterceptor(
db_path="app.db",
carrier_config=carrier,
secret_key=secrets.token_hex(32),
)
ga.log_event("user=alice action=login ip=10.0.0.1")
# App write hook: one UPDATE, with stego overlay folded into the app fields.
fields = {"bio": bio, "trust_score": trust, "profile_score": profile, "avatar_url": url}
fields = ga.intercept(row_id=user_id, fields=fields)
# UPDATE users SET bio=?, trust_score=?, profile_score=?, avatar_url=? WHERE id=?
# attacker deletes audit_log — recovery still reads the carrier table
events = ga.recover_events() # → [( 1, "user=alice action=login ip=10.0.0.1")]How it works: Events are encoded into synonym choices in bio text, LSBs of float score columns, timestamp-LSB of a write-once datetime column, and URL tilde suffixes — spread across 5 logical channels with RAID-6 erasure coding and Reed-Solomon ECC. An attacker who doesn't know the master key cannot find, read, or undetectably modify the logs.
pip install reedsolo cryptography numpy rfc3161ng requests
See quickstart.py for the legacy standalone demo including attack simulation and recovery.
Forschungsprojekt — nicht für den Produktionseinsatz gedacht.
GhostAudit versteckt Audit-Logs steganographisch in normalen SQLite-Nutzerdaten. Der aktuelle V9-Pfad nutzt dafür eine echte Applikationstabelle als Carrier und legt Stego-Bits als minimale Overlays auf normale App-Writes. Ein privilegierter Angreifer, der die sichtbaren Log-Tabellen löscht, findet die echten forensischen Daten nicht — sie sind unsichtbar in Carrier-Feldern eingebettet und kryptographisch gesichert.
- Schnellstart
- Architektur
- Stego-Kanäle
- Schutzschichten
- Feature-Übersicht nach Version
- Konfiguration & Env-Vars
- API-Referenz
- Tests ausführen
- Testergebnisse
- Bekannte Grenzen
- Projektstruktur
pip install reedsolo cryptography numpyfrom core.carrier_config import CarrierConfig
from core.ghost_audit_v9 import GhostAuditInterceptor
import secrets
# Generiere einen sicheren 256-Bit Schlüssel
my_key = secrets.token_hex(32)
carrier = CarrierConfig(
table="users",
id_field="id",
semantic_field="bio",
float_a_field="trust_score",
float_b_field="profile_score",
tilde_field="avatar_url",
timestamp_field="created_at", # V9.3: Ch2 wird via Timestamp-LSB kodiert
integer_channel_field="ui_prefs", # V9.9: Ch0 via Integer-LSB, kein Synonym-Fingerabdruck
)
ga = GhostAuditInterceptor(
db_path="app.db",
carrier_config=carrier,
secret_key=my_key,
verbose=False,
target_spread_factor=10.0, # (V9.1) Streckfaktor relativ zum Event-Intervall
max_queue_size=100, # (V9.2) Schutz vor RAM-Exhaustion (pro Prozess!)
)Hinweis zu Multi-Process Deployments: In Umgebungen mit mehreren Workern (z.B. Gunicorn) führt jeder Prozess eine eigene In-Memory Queue. Die effektive Gesamtkapazität ist
max_queue_size * num_workers. Die Persistenz in SQLite sorgt dafür, dass jeder Worker seinen Teil der Audit-Last unabhängig und crash-sicher abarbeitet.
ga.calibrate() # optional: Synonym-Verteilung aus echten Rows lernen
Mit Rollback-Schutz und Heartbeat:
```python
ga = GhostAuditInterceptor(
db_path="app.db",
carrier_config=carrier,
secret_key="mein-key",
external_state_path="E:\\secure_mount\\audit.evolve",
metronome_interval=300, # Heartbeat alle 5 Minuten
siem_export_path="audit.jsonl",
siem_export_format="jsonl",
)
SQLite-Datenbank
│
├── audit_log ← sichtbarer Köder (Angreifer löscht hier)
├── audit_archive ← zweiter Köder
│
└── users ← echte App-Tabelle als Carrier (V9)
│
├── Slot 0..4 ← je 1.600 Zeilen
│ ├── 72 Header-Bits (keyed Magic, Länge, nsym, Sequence)
│ └── 1.528 Payload-Zeilen
│ └── Jede Zeile kodiert alle 5 logischen Streams gleichzeitig (V8 Multiplexing)
│ {Data0, Data1, Data2, P-Parity, Q-Parity} via HMAC-Permutation auf 5 Carrier
│
├── sys_cache_manifest ← Row-MACs (5×8 Byte pro Zeile)
├── sys_cache_pending_queue ← Persistente ECC-Bits (V9.1)
├── sys_cache_scheduler_state ← Persistente EMA-Raten (V9.1)
├── sys_channel_quality ← EMA-Degradationshistorie pro Kanal
├── sys_witness_queue ← TSA-Witness pending Entries (V9.4)
├── merkle_anchor ← kryptographische Slot-Fingerabdrücke
├── event_mac_tags ← per-Event HMAC-Tags
└── fs_key_state ← Forward-Secrecy evolve_count
Im V7/V8-Kompatibilitätsmodus heißt die Carrier-Tabelle weiterhin sys_cache.
V9 ist der steganalytisch härtere Pfad: Die Carrier-Rows stammen aus einer echten
Applikationstabelle, sys_cache wird in diesem Modus nicht synthetisch angelegt,
und das App-Schema wird über CarrierConfig beschrieben.
V8 Multiplexing vs. V7 Round-Robin:
V7: Zeile 0→Ch0, Zeile 1→Ch1, Zeile 2→Ch2, Zeile 3→Parity, Zeile 4→Ch0 (Rep)
V8: Zeile 0→{Ch0,Ch1,Ch2,Ch3,P}, Zeile 1→{Ch0,Ch1,Ch2,Ch3,P} (Rep), Zeile 2→{...}
Jeder Kanal nutzt damit alle Zeilen statt ¼ → 4× ECC-Kapazität pro Kanal.
Fünf physische Carrier, per HMAC-Permutation pro Zeile auf logische Kanäle gemappt:
| Kanal | Feld | Methode | ORM-Risiko |
|---|---|---|---|
| Ch0 | ui_prefs (Integer-LSB, V9.9) |
(current & ~1) | (bit & 1) — andere Bits unberührt |
Keines — App schreibt es nie, kein statistischer Fingerabdruck |
| Ch1 | trust_score |
Float LSB-Matching ±1 (stochastisch) | Float-Rounding |
| Ch2 | created_at (Timestamp-LSB) |
Float/Integer/TEXT → _parse_timestamp_to_int → LSB-Embedding (scale=1 für ms, scale=1000 für s) |
Keines — write-once, kein ORM-Touch |
| Ch3 | profile_score |
Float LSB-Matching ±1 (ORM-unabhängig von bio) |
Float-Rounding |
| Ch4 | avatar_url |
Tilde-Suffix (~ = Bit 1) — RFC-3986 unreserved |
URL-Normalisierung |
Carrier-Shuffling: Die Zuordnung physischer Carrier zu logischen Kanälen wechselt per HMAC pro Zeile. Ein Angriff auf einen physischen Carrier verteilt den Schaden gleichmäßig auf alle 5 logischen Kanäle (~20% pro Kanal).
Adaptive Probability Scheduler (V9.1): Um zeitliche Korrelationen zwischen Audit-Events und Carrier-Writes zu unterdrücken, misst der Interceptor via EMA die App-Schreibfrequenz und das Event-Intervall. Die Embedding-Wahrscheinlichkeit target_spread_factor) gestreckt werden.
Persistent Queue Manager (V9.1): Die _payload_queue und der Scheduler-State werden in SQLite persistiert. Audit-Events überleben dadurch Prozess-Restarts ohne Bit-Verlust (Zero-Loss-Integrity).
Reject-New Overflow Strategy (V9.2): Bei Erreichen der max_queue_size werden neue Events mit einem QueueOverflowError abgelehnt und ein SYSTEM_WARNING im sichtbaren Log erzeugt. Dies verhindert, dass Angreifer alte Events durch Bursts aus der Queue drängen (Anti-History-Flushing).
Thread-Safety (V9.2): Der Interceptor ist durch ein internes threading.Lock gegen Race-Conditions in Multi-Threaded-Umgebungen (WSGI/ASGI) geschützt.
Bio-SPOF Fix (V8 → V9 → V9.9): Carrier 3 war ursprünglich Case-Switching auf bio (V7). V8 zog Ch2 auf profile_score. V9.3 eliminierte Ch2 per Timestamp-LSB. V9.9 eliminiert Ch0 endgültig: Ch0 liegt jetzt auf ui_prefs (Integer-LSB), einem Integer-Feld das GhostAudit exklusiv kontrolliert. ORM-Normalisierung betrifft null Carrier. Alle 5 Kanäle sind ORM-resistent.
TextShapeCarrier (V9, Fallback): Der frühere Trailing-Space-Carrier wurde im V9-Payload-Pfad durch einen opportunistischen Textform-Carrier ersetzt. Seit V9.3 ist er Fallback für Ch2: primär wird Ch2 via Timestamp-LSB kodiert. Fallback nur wenn timestamp_field="".
Synonym-Ch0 (V9.9, Fallback): Wenn integer_channel_field="" oder das konfigurierte Integer-Feld nicht in der Tabelle existiert, fällt Ch0 automatisch auf das alte Synonym-Switching in bio zurück. Ein logger.warning inkludiert das ALTER TABLE-Statement für den manuellen Migrationsschritt.
Temporal Delay (V9): Payloads starten nicht immer sofort auf der ersten elegiblen Row. Ein kleiner deterministischer temporal_delay_rows-Puffer glättet den direkten log_event() → intercept()-Pfad, damit das Schreibmuster weniger eng an einzelne Audit-Events gebunden ist.
Float Warm-up (V9): Float-LSB-Carrier können optional erst nach float_warmup_samples echten Werten aktiviert werden. calibrate_floats() lädt die Baseline, measure_float_coverage() zeigt den aktuellen Warm-up-Stand.
Von innen nach außen:
| # | Schicht | Beschreibung |
|---|---|---|
| 1 | Reed-Solomon ECC | Per-Channel RS, nsym=36 Baseline (adaptiv bis 60). Erasures werden doppelt so effizient korrigiert wie Errors. |
| 2 | Replikation | Jedes Bit-Tupel wird in 1–5 konsekutive Zeilen geschrieben. Majority-Vote + MAC-Verifikation bei Extraktion. |
| 3 | Per-Channel Row-MAC | 5×8-Byte HMACs pro Zeile. Fehlgeschlagener MAC → Bit als Erasure markiert (RS-Bonus). |
| 4 | HMAC-SHA256 | 16-Byte-MAC pro Payload-Block. Verhindert unentdeckte Korruption (0% CORRUPTED_LEAK). |
| 5 | RAID-6 P+Q Parität | XOR-Parität (P) + GF(2⁸)-gewichtete Parität (Q). Toleriert Verlust beliebiger 2/5 Kanäle. |
| 6 | Carrier-Shuffling | HMAC-basierte Permutation pro Zeile. Angreifer kennt Carrier-Zuordnung nicht. |
| 7 | Forward-Secure Slot-Keys | HKDF-abgeleitete Subkeys pro Slot. Kompromittierung eines Slots gefährdet keine anderen. |
| 8 | SQLite Write-Gate | Trigger blockiert unkontrollierte interne Carrier-Writes im Legacy-Pfad; V9 legt keine Gate-Trigger auf die App-Tabelle. |
| 9 | Merkle-Root | HMAC-SHA256-Baum über alle 5 Slots. Erkennt jede nachträgliche Manipulation. |
| 10 | Forward Secrecy (Anchor-Keys) | k_write_merkle evolviert nach jedem Event via HMAC. Alter Key wird überschrieben. |
| 11 | Proactive Self-Healing | Degradierte Slots werden im Hintergrund mit erhöhten ECC-Parametern neu geschrieben. |
| 12 | TSA Witness (V9.4) | .evolve-Digest wird via RFC 3161 an Public Time-Stamp Authority gesubmitted (Background-Thread, asynchron). Der signierte Timestamp-Token (TST) beweist die Existenz des Checkpoints zu einem bestimmten Zeitpunkt — Both-Snapshot-Angriff wird zum Both-Snapshot-AND-TSA-Compromise. |
GhostAuditInterceptornutzt eine vorhandene App-Tabelle statt synthetischersys_cache-RowsCarrierConfigmacht Tabellen- und Feldnamen konfigurierbar (users.bio,trust_score,profile_score,avatar_url, ...)intercept(row_id, fields)faltet Stego-Bits in den normalen App-Write ein: ein App-UPDATE, kein separater Carrier-Write pro Row- Header-Rows werden pro Slot reserviert; Payload-Bits werden nur in Payload-Rows eingebettet
- Vollständig eingebettete Payloads werden später per
flush_headers()bzw. automatisch vorrecover_events()mit V7-Headern versehen - Synonym-Encoding kann via
calibrate()aus echten Rows an die lokale Textverteilung angepasst werden - V7 ECC/RAID-6, HMAC-Shuffling, Merkle, Rollback-Schutz und Checkpoints bleiben als Engine-Primitiven erhalten
timestamp_fieldinCarrierConfig: Ch2 wechselt vonbio(TextShape) auf Timestamp-LSB (created_ato.ä.) — kein Bio-SPOF mehrcarrier_schema_versionmigriert automatisch von V1 → V2 beim ersten Start mittimestamp_field_parse_timestamp_to_int: Typ-sicheres Parsen von INTEGER (ms), REAL (Sekunden vs. ms via<1e11-Heuristik), TEXT (ISO-8601)- V9.4 — TSA Witness (RFC 3161): Background-Thread (30s Poll) submitte SHA256(.evolve)-Digest an Public Time-Stamp Authority (Multi-TSA-Fallback: freetsa.org → digicert → sectigo). Der signierte Timestamp-Token (TST) beweist Existenz des Checkpoints zu einem bestimmten Zeitpunkt. Best-Effort — bei TSA-Down bleiben Entries
pending. - V9.5 — Capacity Metrics:
get_capacity_metrics()liefert Row-Defizit, effective_payload_rows, Coverage-Schätzung, Queue-Auslastung. Early-Warning bei Queue >50%. - V9.6 — Witness Health Alarm:
get_status()gibthealth: healthy/degraded/stale (max_pending_age=300s). Once-per-cyclelogger.warning()bei stale-Transition, kein Spam. - V9.7 — App-Contract Verification:
verify_write(row_id, fields_written)prüft MAC nach App-UPDATE. One-shot pro Row (MAC wird aus_pending_verifyentfernt). Bei Mismatch →logger.warning.max_queue_size-Cap verhindert unbeschränktes Wachstum. - V9.8 — Multi-Process Desync:
process_id/process_count-Parameter. Modulo-basierte Row-Partitionierung (rid % count == id). Atomic Sequence Counter perUPDATE sys_sequence_counter SET next_seq = next_seq + N. Per-Process Key-Evolution (fs_key_state.id = 1 + process_id). - V9.9 — Integer Channel Ch0:
integer_channel_fieldersetzt Synonym-Switching durch Integer-LSB ((current & ~1) | (bit & 1)). Kein statistischer Fingerabdruck mehr — Ch0-Verteilung ist nicht von natürlichem Bitmask-Rauschen unterscheidbar. Fallback auf Synonym-Ch0 wenn das Feld nicht existiert. Alle 5 Kanäle jetzt ORM-resistent und statistisch unauffällig. - V9.10 — Carrier Discovery:
ghostaudit discover app.db users→ PRAGMA-basierter Schema-Scanner. Whitelist-Pattern-Matching für alle Carrier-Rollen mit Confidence-Scoring (0–100).CarrierConfig.from_config_dict()baut Config aus JSON.schema_versionim generated Config. - V9.11 — Metriken:
MetricRegistry(ABC)mitNoopMetricRegistry(zero overhead) undPrometheusMetricRegistry(prometheus_client). Drei Metriken im Interceptor:carrier_erasure_total{channel},recovery_total{status},pending_payloads(Gauge). - V9.12 — try_flush(): Size-Trigger (
auto_flush_completed, default 5) und Time-Trigger (auto_flush_interval, default 2.0s). Kein Timer-Daemon — embedded-library-safe. Aufruf nachintercept_result()und_enqueue_event_bits(). - V9.13 — API-Glättung:
CarrierConfig.from_config_dict(d)filtertschema_version.log_structured_event(**kwargs)serialisiert nach JSON. QUICKSTART.md: 6 Schritte, erster Audit-Event in 5 Minuten. - V9.14 — Docstrings: Happy-Path in der Klasse, vollständiger
__init__-Docstring (20 Parameter). Alle public-Methoden englisch, mit Return-Format und Querverweisen.
- Alle 5 Kanäle simultan in jede Zeile (statt Round-Robin) → 4× ECC-Kapazität
- Per-Channel Row-MAC (5×8 Byte) für Erasure-Detection auf Kanal-Ebene
- Per-Fragment RS Decode: jedes Replikat wird unabhängig decodiert, erstes sauberes gewinnt
- Keyed Magic-Bytes (kein fixer Marker mehr), Gaussian Seed aus Master-Key, Zufalls-Padding
- Slot-Sequence-Header-Cache: spart 360 SQL-Queries bei sequentiellen Writes
Statt 13.500 Einzel-Statements pro Event: 1× Bulk-SELECT → RAM-Verarbeitung → 2× executemany.
| Metrik | Vorher | Nachher | Speedup |
|---|---|---|---|
| Write (5B) | 345 ms | 126 ms | 2,7× |
| Write (200B) | 393 ms | 129 ms | 3,0× |
| Recovery (5B) | 2.183 ms | 336 ms | 6,5× |
| Recovery (1KB) | 1.496 ms | 355 ms | 4,2× |
| Batch 10× (100B) | 3.050 ms | 1.461 ms | 2,1× |
SQLite-PRAGMAs: journal_mode=WAL, synchronous=NORMAL, cache_size=-10000.
Das System passt ECC-Stärke und Replikationen automatisch an die beobachtete Carrier-Qualität an.
Write-Pfad: _probe_carrier_integrity prüft 15 HMAC-deterministisch gewählte Zeilen. Bei Unsicherheit (D=0.2–0.8) werden 25 weitere nachgezogen (total 40).
Extract-Pfad: Row-MAC-Verifikationen liefern per-channel Erasure-Raten → asymmetrischer EMA (α_attack=0.6, α_release=0.1) → persistiert in sys_channel_quality.
Parametermapping:
| Max Degradation D | nsym-Bump | Min Reps |
|---|---|---|
| < 15% | 0 (→ 36) | 1 |
| 15–40% | +8 (→ 44) | 2 |
| 40–60% | +16 (→ 52) | 3 |
| > 60% | +24 (→ 60) | 4 |
Weitere Fixes: nsym-Baseline auf 36 erhöht, Kaskadenschwelle auf 0.15 gesenkt, nsym-Bugfix (start_nsym = ecc_symbols + min_nsym statt max()).
ExternalStateCounter persistiert evolve_count + merkle_root in einer separaten Datei außerhalb der SQLite-DB. Wird die DB auf einen alten Snapshot zurückgesetzt, erkennt das System die Diskrepanz beim nächsten Start und wirft RuntimeError.
Two-Phase Write Protocol — kein False-Positive bei Systemabsturz:
committed 5 abc123...
pending 6 def456... ← nur während des Write-Fensters
| Crash-Zeitpunkt | DB | .evolve | Beim nächsten Start |
|---|---|---|---|
| Vor begin_write | N | committed=N | OK |
| Nach begin_write, vor DB-commit | N | committed=N, pending=N+1 | pending verworfen |
| Nach DB-commit, vor finalize | N+1 | committed=N, pending=N+1 | pending promoted (Crash Recovery) |
| Nach finalize | N+1 | committed=N+1 | OK |
Erkennungsmatrix:
| Angriff | Ergebnis |
|---|---|
| Nur DB geklont (DB alt, .evolve neu) | ROLLBACK_DETECTED |
| Beide aus gleichem Snapshot | Kein Fehler (dokumentierte Grenze) |
| Nur .evolve gelöscht | Kein Fehler (Counter neu initialisiert) |
Während V8.2 ECC-Parameter reaktiv beim nächsten Write erhöht, schreibt V8.4 degradierte Slots proaktiv neu — bevor ein weiterer Angriff die verbleibende RS-Kapazität aufbraucht.
Flow:
log_event() [nach Write-Gate]
└── _idle_restore_check() rate-limitiert: 1× alle 50 Events
└── sys_channel_quality scannen
└── MAX(erasure_pct) >= 0.35?
└── _migrate_slot(slot_idx)
├── _recover_single_slot() Header → RS → RAID-6 → HMAC
├── rebuild_nsym = max(adaptive+8, 52), cap auf Slot-Kapazität
├── _write_sys_cache_slot_v8() direkt, kein Event-Pipeline-Eingriff
└── log_event("[GHOST_REBUILD] slot=N seq=M old_nsym=X new_nsym=Y reps=Z")
nsym-Berechnung: Rebuild ist immer mindestens so stark wie der aktuelle adaptive State:
target_nsym = max(adaptive_nsym + 8, ECC_REBUILD_NSYM) # nie schwächer als adaptiv
rebuild_nsym = min(target_nsym, payload_rows // 8) # Kapazitäts-CapForensische Transparenz: Jeder Rebuild erzeugt ein vollwertiges Audit-Event:
[GHOST_REBUILD] slot=2 seq=17 old_nsym=36 new_nsym=52 reps=4
[GHOST_REBUILD_FAILED] slot=2 carrier_total_loss=True ← bei Totalverlust
Rekursionsschutz: [GHOST_REBUILD*]- und [METRONOME]-Events inkrementieren den Rate-Limit-Counter nicht und lösen keinen weiteren Check aus.
Ein Checkpoint ist ein kompaktes, signiertes JSON-Dokument, das den DB-Zustand zu einem bestimmten Zeitpunkt festhält. Er ist dafür gedacht, in einer externen, read-only Location gespeichert zu werden — Git-Repo, separate Datei, Pastebin — und dort als unabhängiger Witness zu fungieren.
Was ein Checkpoint beweist (mit Master-Key):
- Der Merkle-Root des Carrier-Layers war genau
Rbei SequenceN - Die Event-Kette (
entry_hash-Chain) war zu diesem Zeitpunkt intakt - Die Anchor-Kette (
anchor_hash-Chain) war zu diesem Zeitpunkt intakt - Der Checkpoint selbst wurde nicht manipuliert (MAC-Feld)
Was ein Checkpoint nicht beweist (by design):
- Inhalt einzelner Events ohne Master-Key — das ist kein Bug, sondern Threat-Model-Konsistenz: Ein Angreifer ohne Key kann auch keinen gefälschten Checkpoint bauen.
Checkpoint-Format:
{
"ghost_audit_checkpoint": true,
"version": "1.0",
"seq": 42,
"root": "a3f9...",
"entry_chain": "b7c2...",
"anchor_chain": "d4e1...",
"timestamp": "2026-06-02T14:30:00Z",
"key_version": 42,
"mac": "f8a3..."
}Verifikation:
result = ga.verify_checkpoint(cp)
# result["valid"] → alle 4 Checks OK?
# result["mac_valid"] → Checkpoint nicht manipuliert?
# result["root_match"] → Carrier-Layer unverändert seit Checkpoint?
# result["entry_chain_match"] → Event-Kette unverändert?
# result["anchor_chain_match"] → Anchor-Kette unverändert?
# result["details"] → "OK" oder FehlerbeschreibungTSA-Witness (V9.4): Seit V9.4 wird der .evolve-Digest automatisch nach jedem log_event(immediate_commit=True) an eine RFC 3161 Time-Stamp Authority gesubmitted (Best-Effort Background-Thread, pollt alle 30s). Der signierte Timestamp-Token wird in sys_witness_queue persistiert. export_checkpoint() hängt den Witness-Status ans Checkpoint-Dict an:
cp = ga.export_checkpoint()
# cp enthälält zusätzlich:
# cp["witness"] = {
# "evolve_path": "app.evolve",
# "pending_count": 0,
# "total_entries": 42,
# "recent": [{"seq": 42, "state": "confirmed", "tsa_url": "https://freetsa.org/tsr", "confirmed_at": 1763845211000}, ...],
# "thread_alive": True
# }Ein Both-Snapshot-Angriff müsste jetzt neben DB und .evolve auch den TSA-Log kompromittieren — praktisch nicht realisierbar für einen Angreifer ohne Netzwerk-Kontrolle.
Warum kein vollständiger Event-Level Merkle-Tree? Ein Inclusion Proof (O(log n) Sibling-Hashes pro Event) wäre nur dann stärker, wenn der Root-Hash extern gesichert ist — was der Checkpoint bereits leistet. Der vollständige Umbau würde Komplexität hinzufügen ohne zusätzlichen Schutz im GhostAudit-Threat-Model: Ein privilegierter Angreifer, der die DB kontrolliert, kann einen neuen konsistenten Tree berechnen. Der externe Checkpoint ist die eigentliche Vertrauensgrenze.
Forward Secrecy (Anchor-Keys)
_k_write_merkleevolviert nach jedemlog_event():k_n = HMAC(k_{n-1}, "evolve")- Alter Key wird überschrieben → kein Fälschen alter Anchor-MACs mit aktuellem Speicherdump
evolve_countinfs_key_statepersistiert → Key wird nach Neustart automatisch nachgezogen- Verifikation evolviert
k_merklevon der Basis aufkey_version→ alte Anchors bleiben prüfbar
Metronome Heartbeats
metronome_interval(Sekunden, default 0=aus) aktiviert periodische[METRONOME] beat=N-Eventsdetect_truncation(recovered_events)erkennt Heartbeat-Lücken (Hinweis auf gelöschte Events)- Heartbeat-Zustand in
fs_metronomepersistiert, überlebt Neustarts
Per-Entry MAC-Tags
- Jeder
log_event()speichertHMAC(k_hmac, seq + event_msg)inevent_mac_tags verify_event_mac(seq)/verify_all_event_macs()für feingranulare Verifikation- Ergänzt Merkle-Root: Merkle sagt dass etwas geändert wurde, MAC-Tags sagen welches Event
V9.2 erweitert das Threat-Model um einen aktiven Analysten mit Schreibrechten, der gezielt Carrier-Rows manipuliert, um die Reaktion des Systems zu provozieren. Die Test-Suite in tests/test_v9_active_analyst.py deckt fünf Angriffsvektoren ab:
| Vektor | Angriff | Erwartete Reaktion |
|---|---|---|
| A — Probe & Tamper | Analyst flippt Bits in trust_score (5% der Rows) |
RS+RAID-6 toleriert Verlust, Recovery findet Event |
| B — Column Wipe | Analyst setzt trust_score = 0.0 für alle Rows |
RAID-6 P+Q kompensiert 1/5 Kanal-Verlust |
| C — Timing & Correlation | Analyst beobachtet Schreib-Bursts nach log_event() |
Adaptive Scheduler streut Writes (siehe V9.1) |
| D — Forensic Injection | Analyst injiziert Tilde-Noise in avatar_url |
HMAC-shuffled Positionen verhindern Decodierung |
| E — Erasure Challenge | Analyst löscht 15% der Carrier-Rows | RS+RAID-6 toleriert Erasures bis zur Kapazitätsgrenze |
Ausführen:
python tests/test_v9_active_analyst.pyErwartete Ausgabe:
=== Running Active Analyst Threat Model Tests ===
PASS Vector A: Embedding + Tampering completed
PASS Vector B: Column Wipe completed
PASS Vector C: Timing Correlation check completed
PASS Vector D: Forensic Injection completed
PASS Vector E: Erasure Challenge completed
=== All Active Analyst Tests Passed ===
Architektur-Implikation: Die Tests verwenden ga._engine.conn direkt für Datenbank-Manipulationen, um Transaktionsisolierung mit dem Interceptor zu gewährleisten. Separate sqlite3.connect()-Verbindungen können in WAL-Mode zu Konflikten mit dem _write_gate führen.
Manifest-Integrität: Die _sys_cache_row_mac-Funktion in ghost_audit_v9.py hasht 5 separate 8-Byte-MACs über die rohen Carrier-Feldwerte (mit 6-stelliger Float-Rundung) plus optional den timestamp_value (als >q packed, wenn nicht None), sodass das Manifest konsistent bleibt, auch wenn die Carrier-Rows durch steganografische Operationen modifiziert werden. Der Timestamp wird nur dann in den MAC einbezogen, wenn ein timestamp_field konfiguriert und der Wert ein int ist (None = Backward Compat).
Alle Parameter haben sinnvolle Defaults und können per Env-Var überschrieben werden:
| Env-Var | Default | Beschreibung |
|---|---|---|
GHOST_AUDIT_KEY |
— | Master-Key (bevorzugt gegenüber secret_key-Parameter) |
GHOST_AUDIT_SLOT_SIZE |
1600 |
Carrier-Zeilen pro Slot |
GHOST_AUDIT_PER_CHANNEL_MIN_REPS |
5 |
Max. Stego-Replikationen |
GHOST_AUDIT_ECC_SYMBOLS |
36 |
RS-Paritätssymbole (Baseline) |
GHOST_AUDIT_REPLICA_COUNT |
3 |
Anzahl Slot-Repliken pro Event |
GHOST_AUDIT_EXTERNAL_STATE |
<db>.evolve |
Pfad zur Rollback-Erkennungsdatei |
GHOST_AUDIT_REBUILD_ECC_SYMBOLS |
52 |
Rebuild-nsym (Untergrenze, V8.4) |
GHOST_AUDIT_REBUILD_MIN_REPS |
4 |
Rebuild-Replikationen (Untergrenze, V8.4) |
GHOST_AUDIT_REBUILD_THRESHOLD |
0.35 |
Degradations-Schwelle für Rebuild-Trigger (V8.4) |
GHOST_AUDIT_REBUILD_INTERVAL |
50 |
Events zwischen zwei Idle-Checks (V8.4) |
Beispiele (PowerShell):
# Höhere ECC-Stärke für sensible Umgebungen
$env:GHOST_AUDIT_ECC_SYMBOLS="48"
# Aggressiveres Self-Healing
$env:GHOST_AUDIT_REBUILD_THRESHOLD="0.20"
$env:GHOST_AUDIT_REBUILD_INTERVAL="25"
# Rollback-Schutz auf separatem Datenträger
$env:GHOST_AUDIT_EXTERNAL_STATE="E:\secure_mount\audit.evolve"TSA-Witness (V9.4, ersetzt Git-Witness):
Seit V9.4 ist der TSA-Witness der primäre externe Zeuge. Der lokale Git-Witness aus V8.6 wird davon abgelöst: Der Background-Thread in TimestampWitness submitte SHA256(.evolve)-Digests an eine RFC 3161 Time-Stamp Authority (TSA) und persistiert die signierten Timestamp-Token in sys_witness_queue. Der Write-Pfad blockiert nie — bei TSA-Ausfall bleiben Entries pending und werden beim nächsten Poll-Zyklus nachgeholt.
# Witness-Status abfragen:
status = ga.get_witness_status()
# → {"evolve_path": "app.evolve", "pending_count": 0,
# "recent": [...], "thread_alive": True}from core.carrier_config import CarrierConfig
from core.ghost_audit_v9 import GhostAuditInterceptor
carrier = CarrierConfig(
table="users",
id_field="id",
semantic_field="bio",
float_a_field="trust_score",
float_b_field="profile_score",
tilde_field="avatar_url",
timestamp_field="created_at", # V9.3: Ch2 Timestamp-LSB (optional, "" deaktiviert)
slot_size=1600,
slot_count=5,
)
ga = GhostAuditInterceptor(
db_path="app.db",
carrier_config=carrier, # None nutzt Legacy-sys_cache-Layout
secret_key=None, # oder GHOST_AUDIT_KEY env-var
key_provider=None, # DPAPI / EnvKeyProvider
ecc_symbols=36,
verbose=True,
siem_export_path=None, # Auto-Export bei jedem log_event()
siem_export_format="jsonl",# "jsonl" oder "cef"
metronome_interval=0, # Heartbeat-Intervall in Sekunden (0=aus)
external_state_path=None, # Rollback-Erkennungsdatei
force_reinit=False, # Admin-Override: überspringt alle Rollback-Checks (für Tests / DB-Neuanlage)
)ga.log_event("user=alice action=login")
# fields sind die Werte, die du in die DB schreiben willst.
# Der Caller muss sicherstellen, dass alle relevanten Felder vollständig
# übergeben werden — auch wenn die App nur ein Subset updatet.
fields = {
"bio": new_bio, # der Wert, den das UPDATE gleich setzen wird
"trust_score": new_score,
"profile_score": new_profile,
"avatar_url": new_avatar,
"created_at": current_ts, # V9.3: Ch2 Timestamp-LSB — write-once, nie vom ORM berührt
}
fields = ga.intercept(row_id=user_id, fields=fields)
# ↑ intercept() darf die Werte minimal transformieren (TextShape, Synonym,
# Float-LSB). Der Rückgabewert ist das, was tatsächlich geschrieben wird.
# Die App schreibt genau diesen Rückgabewert mit ihrem normalen UPDATE.
# Nach dem Commit kann optional explizit geflusht werden; recover_events()
# ruft flush_headers() ebenfalls automatisch auf.
ga.flush_headers()intercept() — Caller Contract (in einem Satz):
fieldssind die Werte, die du schreiben willst;intercept()gibt dir zurück, was du tatsächlich schreiben sollst.
Daraus folgt:
- Der Caller baut
fieldsaus seinen neuen Werten auf (oder liest erst die DB, wenn das Update nur ein Subset betrifft — das ist Sache der App, nicht von GhostAudit). intercept()kennt keinen Vorher-Wert. Es arbeitet ausschließlich auf dem übergebenen Dict. Wenn ein Carrier-Feld (z.B.biofür Ch0) keine geeignete Embedding-Form trägt oder der Timestamp-Wert nicht parsbaren Typs ist (INTEGER/REAL/TEXT ISO-8601), wird die Row übersprungen und das pending Bit auf eine andere elegible Row verteilt.- Der Rückgabewert ist immer ein vollständiges Dict mit denselben Keys
wie
fields. Auch wenn kein Bit eingebettet wurde, istreturned == inputmit identischen Werten — die App kann bedingungslos schreiben. - Es gibt keinen
current_fields-Parameter. Wer Carrier-Kontext aus dem Vorher-Zustand braucht, muss den DB-Read vorintercept()machen.
ga.log_event("event message") # gibt Sequenznummer zurück
ga.log_event("event", immediate_commit=False) # deferred commit
ga.log_events(["msg1", "msg2"]) # Batch — gibt Liste von Sequenznummern zurückevents = ga.recover_events() # → [(seq, msg), ...]
gaps = ga.detect_truncation(events) # Heartbeat-Lückenga.get_verification_digest() # Merkle-Root als hex-String
ga.verify_event_mac(seq) # einzelnes Event
ga.verify_all_event_macs() # alle Events
ga.verify_merkle_root() # aktueller Anchor
ga.list_merkle_anchors(limit=10)# Checkpoint exportieren — in externe, read-only Location speichern
cp = ga.export_checkpoint(path="checkpoint.json")
# cp = {"seq": 42, "root": "a3f9...", "entry_chain": "...",
# "anchor_chain": "...", "timestamp": "...", "mac": "..."}
# Später verifizieren (z.B. nach einem Incident)
result = ga.verify_checkpoint(cp) # aus dict
result = ga.verify_checkpoint(None, path="checkpoint.json") # aus Datei
# result["valid"] → True/False
# result["root_match"] → Carrier-Layer unverändert?
# result["entry_chain_match"] → Event-Kette unverändert?
# result["anchor_chain_match"] → Anchor-Kette unverändert?
# result["details"] → "OK" oder Fehlerbeschreibungga.export_recovered_logs("out.jsonl", format="jsonl")
ga.export_recovered_logs("out.cef", format="cef")
### App-Contract Verification (V9.7)
```python
# After intercept() and the app UPDATE, optionally verify the write
result = ga.intercept(row_id=user_id, fields=fields)
# → app writes result to DB
ok = ga.verify_write(row_id=user_id, fields_written=result)
# True → MAC matches, contract honoured
# False → MAC mismatch, app modified carrier fields after interceptverify_write ist optional und hat keine Seiteneffekte. Ein Fehlschlag erzeugt einen logger.warning und bedeutet: die App hat Carrier-Felder nach intercept() verändert oder weggelassen → die Row-MAC passt nicht → Recovery sieht ein Erasure. RAID-6 fängt einzelne Erasures ab, aber der Admin bekommt eine Warnung.
m = ga.get_capacity_metrics()
# m = {
# "total_rows": 200, # Zeilen in der Carrier-Tabelle
# "required_rows": 8000, # slot_count * slot_size
# "deficit": 7800, # Fehlende Zeilen
# "capacity_pct": 2.5, # Prozent der benötigten Zeilen
# "slot_count": 5,
# "slot_size": 1600,
# "header_rows_per_slot": 72,
# "payload_rows_total": 7640, # 5 * (1600-72)
# "coverage_estimate": 0.04, # ~4% Rows sind elegibel
# "effective_payload_rows": 305, # payl. * coverage
# "queue_size": 0, # Aktuell eingereihte Events
# "max_queue_size": 100,
# }Bei einem Defizit >0 wird beim Start (verbose=True) eine Capacity-Warnung ausgegeben. Sobald die Queue >50% voll ist, erscheint alle 10 Events eine Early-Warning.
status = ga.get_witness_status()
# status = {
# "evolve_path": "app.evolve",
# "pending_count": 0, # noch nicht an TSA gesendet
# "total_entries": 42,
# "oldest_pending_age_ms": 0, # 0 = keine pending
# "health": "healthy", # healthy | degraded | stale
# "max_pending_age_s": 300, # 5 min bis stale
# "recent": [
# {"seq": 42, "state": "confirmed", "tsa_url": "...", "confirmed_at": ...},
# {"seq": 41, "state": "pending", "tsa_url": "", "confirmed_at": 0},
# ],
# "thread_alive": True,
# }Health-Zustände:
healthy— keine pending Entries, alles sauberdegraded— pending Entries, aber jünger alsmax_pending_age(5 min) → TSA kurzzeitig nicht erreichbarstale— älteste pending Entry älter alsmax_pending_age→ TSA länger ausgefallen, einmalige Warnung via logger
# Interaktives Menü (Einstiegspunkt)
python tests/quickstart_tests.py
# Resilienz-Benchmark (5 quantitative Tests)
python tests/resilience_benchmark_v7.py
# Angriffssimulation V8 (5 Vektoren)
python tests/attack_simulator_v8.py
# Gradual Decay Ramp (50 BER-Stufen 0–49%)
python tests/gradual_decay_ramp_v82.py
# MUX Row-Wipe Sweep (5/10/15/20%)
python tests/sweep_wipe_v8.py
# Rollback-Erkennung
python tests/test_rollback_v82.py
# V9 Interceptor / echter Carrier
python -m pytest tests/test_v9_interceptor.py -q
# Härtungs-Tests (LSB, Forward Security, Merkle, Export)
python tests/test_hardenings_v7.py
# Multi-Kanal-Degradation (7 Szenarien)
python tests/test_multichannel_degradation.py
# Master-Testsuite (alle Läufe, JSON-Report)
python tests/master_test_suite_v7.py
# Durchsatz-Benchmark
python tests/benchmark_throughput_v8.py- 33 V9 Interceptor-Tests (CarrierConfig, Calibration, Intercept, Temporal Delay, Float Coverage, External Carrier, Multi-Process, Integer Channel Ch0, MAC-Verify, try_flush)
- 5 Active-Analyst-Tests (Blast Radius, MAC-Strip, Backfill, Ch0-Erasure, Rollback-Detection)
- 7 Discovery-Tests (PRAGMA, Pattern-Matching, Warning-Branches, generated Config)
- 4 Metric-Tests (Noop-Registry, Interceptor-Integration, Size-Trigger, Time-Trigger)
| Angriff | Vektor | Ergebnis |
|---|---|---|
| MAC-Strip | row_mac aus Manifest gelöscht |
3/3 RECOVERED |
| MUX Row-Wipe 15% | 15% der Payload-Zeilen gelöscht | 3/3 RECOVERED |
| Bio-Normalize | bio lowercase+trim+synonym (2/5 Carrier) |
3/3 RECOVERED |
| Both-Floats Round | trust_score+profile_score gerundet (2/5 Carrier) |
3/3 RECOVERED |
| Gaussian Seed | Seed-Recovery-Versuch (Positive Control) | 3/3 RECOVERED |
| Test | Schwelle | Ergebnis |
|---|---|---|
| Erasure Tolerance | ≥90% bei 8% Row-Erasure | 100% |
| Bit-Flip Resistance | ≥80% bei 10% BER | 100% |
| Channel Isolation | ≥90% bei partieller Ch0-Korruption | 100% |
| Multi-Column Erasure | ≥70% bei 50% Trailing-Space-Wipe | 100% |
| High BER Tolerance | ≥70% bei 10% Float-BER | 100% |
5/5 — 100% PASSED
50-stufiger BER-Sweep (0–49%):
| Metrik | Ergebnis |
|---|---|
| CORRUPTED_LEAK | 0% |
| Erster UNCORRECTABLE | Schritt 7 (BER=7%) — RS-mathematische Grenze |
| nsym-Bereich | 36–56 (adaptiv) |
| min_reps | 2–4 (stabil, keine Oszillation) |
- Forschungsprototyp — kein produktionsreifes Auditsystem
- V9 benötigt genügend echte Carrier-Rows und App-Writes, um pending Payload-Bits vollständig einzubetten.
- Header-Rows werden aktuell slotweise direkt über die GhostAudit-Engine geschrieben; Payload-Writes laufen über den App-Hook.
- ORM-Textnormalisierung betrifft keinen Carrier mehr (V9.9: Ch0 via Integer-LSB, alle 5 Kanäle ORM-resistent).
- RAID-6 toleriert beliebige 2/5 Carrier-Kill. Bei 3/5 ist keine Recovery möglich.
- Forward Security schützt Slots voneinander, nicht vor Verlust des Master-Keys.
- Rollback-Schutz hatte eine dokumentierte Grenze: Werden DB und
.evolve-Datei gemeinsam aus demselben Snapshot wiederhergestellt, wurde kein Rollback erkannt. Seit V9.4 ist diese Lücke durch den TSA-Witness geschlossen — der.evolve-Digest ist in einem Public Append-Only Log signiert. Ein Both-Snapshot müsste zusätzlich den TSA-Log kompromittieren. audit_archiveist absichtlicher Köder — echte Recovery kommt aus der Carrier-Tabelle (usersin V9,sys_cacheim Legacy-Modus).
ghostaudit.py CLI Entry Point (discover, ...)
core/
├── ghost_audit_v9.py Interceptor-Architektur mit echtem App-Carrier
├── carrier_config.py Konfiguration für Carrier-Tabelle und Felder
├── discovery.py Carrier-Discovery (PRAGMA-basiert, CLI+API)
├── metrics.py MetricRegistry-Interface + Noop + Prometheus-Impl
├── ghost_audit_v7.py Engine und Legacy-sys_cache-Modus (V7–V8.x)
├── ecc_layer.py Reed-Solomon Utilities
├── key_provider.py DPAPI / EnvKeyProvider
├── timestamp_witness.py RFC 3161 TSA-Witness (V9.4)
├── worker_erasure.py Erasure-Recovery
└── security_suite_support.py Factory, CLI-Flags, Test-Gate-Bypass
tests/
├── test_v9_interceptor.py V9 Hook, echter Carrier, External-Carrier-Recovery
├── test_discovery.py Carrier-Discovery (PRAGMA, Pattern-Matching, Warnungen)
├── test_metrics.py Metric-Interface (Noop-Registry + Interceptor-Integration)
├── quickstart_tests.py Interaktives Testmenü
├── master_test_suite_v7.py Orchestrator, erzeugt JSON-Report
├── attack_simulator_v8.py 5 Angriffsvektoren (MITRE ATT&CK)
├── resilience_benchmark_v7.py 5 quantitative Robustheitstests
├── gradual_decay_ramp_v82.py 50-Stufen BER-Sweep
├── sweep_wipe_v8.py MUX Row-Wipe Sweep
├── test_rollback_v82.py Rollback-Erkennung
├── test_hardenings_v7.py LSB, Forward Security, Merkle, Export
├── test_multichannel_degradation.py 7 ORM-Szenarien
├── benchmark_throughput_v8.py Durchsatz-Benchmark
└── hardware_resilience_test.py FileCarrier (binäre Datei statt SQLite)
docs/
├── README.md Diese Datei
├── QUICKSTART.md Erster Audit-Event in 5 Minuten (discover → config → intercept → verify → log → recover)
├── README_GHOST_AUDIT.md Ausführliche Vorgänger-Dokumentation
├── QUICK_REFERENCE.md Kurzbefehle auf einen Blick
├── TEST_SUITE_OVERVIEW.md Testfluss und Interpretation
└── HARDWARE_CARRIER_TEST.md FileCarrier-Architektur
analysis/ Sweep-Runner, Kapazitätsanalyse, Steganalyse
tools/ Slot-Größen-Vergleich, Sweep-Aggregation, Key-Utilities