19. Audit Log
Append-only hash chain + SGX-signed checkpoint · cross-DB bindingFireblocks 의 Audit Log 기능 (확인된 사실)
security-checklist.md, p.2: log / track / audit / export workspace events.
기록되는 이벤트 (Fireblocks 자료에서 확인된 것):
- IP allowlist 추가·수정·활성화·비활성화
- Recovery passphrase verification 알림 / 결과 / risk status
- (그 외 lifecycle event 는 본 자료 명시 없음 — 구현체가 광범위 기록한다고 가정)
접근 권한
user-roles.md, p.7 — View all workspace settings including audit logs:
- Owner / Admin / NSA / Security Auditor / Security Admin ✓
Hash Chain + SGX Checkpoint 의 영속화 모델
외부 감사자가 "log 가 변조되지 않았다"를 cryptographically 검증할 수 있어야 institutional-grade. 두 layer:
graph LR
subgraph EVENTS["audit_events (append-only)"]
E1["seq=1
prev_hash=0
current_hash=h1"]
E2["seq=2
prev_hash=h1
current_hash=h2"]
E3["seq=3
prev_hash=h2
current_hash=h3"]
E4["seq=4
prev_hash=h3
current_hash=h4"]
end
subgraph CP["audit_checkpoints"]
C1["checkpoint @ seq=4
checkpoint_hash=h4
sgx_signature
mrenclave"]
end
E1 --> E2 --> E3 --> E4
E4 -.signs.-> C1
EXT["External auditor"]
EXT -.recompute chain.-> EVENTS
EXT -.verify signature.-> C1
Figure 9. Hash chain + SGX checkpoint — 외부 감사자가 hash chain 재계산 + checkpoint signature 검증으로 정합성 입증.
DB Schema
-- ★★ append-only — BEFORE UPDATE / BEFORE DELETE 트리거로 강제 (23 참조)
CREATE TABLE audit_events (
id BINARY(16) PRIMARY KEY,
partition_key VARCHAR(64) NOT NULL, -- e.g., workspace_id + month
seq BIGINT NOT NULL, -- per-partition monotonic
event_type VARCHAR(64) NOT NULL, -- 'user-add', 'tx-broadcast', ...
event_domain VARCHAR(64) NOT NULL, -- 'user', 'transaction', 'policy', ...
source_aggregate_type VARCHAR(64) NOT NULL,
source_aggregate_id BINARY(16) NOT NULL,
source_db VARCHAR(32) NOT NULL, -- 'walletdb', 'ledgerdb', ...
event_payload_cbor BLOB NOT NULL, -- ★ byte-equal cross-DB binding
prev_hash BINARY(32) NOT NULL, -- prior seq 의 current_hash
current_hash BINARY(32) NOT NULL, -- SHA256(prev_hash || payload || seq)
actor_user_id BINARY(16),
occurred_at DATETIME(6) NOT NULL,
UNIQUE KEY (partition_key, seq),
KEY (event_domain, occurred_at),
KEY (source_aggregate_id)
);
-- ★ append-only — SGX/TEE 가 주기적으로 서명
CREATE TABLE audit_checkpoints (
id BINARY(16) PRIMARY KEY,
partition_key VARCHAR(64) NOT NULL,
last_seq BIGINT NOT NULL,
checkpoint_hash BINARY(32) NOT NULL, -- = last seq 의 current_hash
sgx_signature BLOB NOT NULL, -- enclave signature
sgx_mrenclave BINARY(32) NOT NULL, -- enclave measurement
signed_at DATETIME(6) NOT NULL,
UNIQUE KEY (partition_key, last_seq)
);
Hash Chain 동작
seq=1: prev=0, current = SHA256(0 || payload_1 || 1) seq=2: prev=h1, current = SHA256(h1 || payload_2 || 2) seq=3: prev=h2, current = SHA256(h2 || payload_3 || 3) ... 중간 row 변조 시 뒤의 모든 hash 가 깨짐 → 변조 감지
SGX-Signed Checkpoint 의 의미
- 운영자가 hash chain 을 통째로 재계산해서 위조하더라도, SGX enclave 의 서명이 없으면 외부 감사자에게 무효
- MRENCLAVE 가 known-good 값과 일치해야 enclave integrity 확보
- 외부 감사자는 SGX attestation 또는 DCAP 으로 enclave 검증 가능
Cross-DB Binding
audit_events 의 event_payload_cbor 가 다른 DB 의 source row (예: approval_decisions, signing_events, transactions) 와 byte-equal CBOR encoding 일치해야 함. 이로써 cross-DB 일관성도 cryptographically 입증.
-- 검증 query (외부 감사자)
SELECT
a.partition_key,
a.seq,
a.event_payload_cbor,
(SELECT cbor_encode(approver_user_id, decision, decided_at, mobile_device_sig)
FROM approverdb.approval_decisions
WHERE id = a.source_aggregate_id) AS source_payload,
a.event_payload_cbor = source_payload AS payload_matches
FROM auditdb.audit_events a
WHERE a.event_domain = 'approval'
AND a.partition_key = ?;
Append-only Enforcement
Application code 가 evolve 하더라도 DB 가 거절해야 의미가 있음. 23. Storage Discipline 의 trigger 패턴 참조.