Salta ai contenuti

Osservabilità: log SIEM con catena di hash e report di rendering

Il modulo Observability è l’implementazione dello stato a runtime: un log eventi SIEM con catena di hash a prova di manomissione, l’aggregazione dei report di rendering e pilot, un log di audit HSM e un insieme completo di implementazioni no-op per metrics e trace, così che la strumentazione sia sempre richiamabile.

Una sola pagina canonica per ciascun argomento. I contratti di osservabilità — ContextAwareExceptionInterface, SpectrumInterface, JobNotificationInterface e l’enum DegradationPolicy — sono documentati in Contracts / Observability. Questa pagina documenta l’implementazione concreta dello stato a runtime. Le due pagine sono complementari, non duplicati: consultare la pagina dei contratti per l’SPI e questa per il log SIEM, i report e le superfici di audit.

Terminal window
composer require nextpdf/core:^3

Questo modulo è il punto in cui lo stato a runtime del motore diventa un output durevole e verificabile.

HashChainSiemEventLog è la superficie di livello security. Implementa il contratto SiemEventEmitter e scrive un log JSON-Lines in cui l’hash di ogni record è SHA-256(prev_hash_bytes || canonical_event_bytes). Questa catena di hash lineare rende il log a prova di manomissione: modificare un byte qualsiasi, eliminare una riga o riordinare le righe spezza la catena. verifyIntegrity() percorre il file e restituisce l’indice del primo record incoerente, oppure null se la catena è integra. readAll() trasmette i record in streaming. Un flock(LOCK_EX) advisory per processo racchiude la sezione critica leggi-coda-poi-accoda, così che processi PHP concorrenti sullo stesso file non intercalino i record. Il design è esplicito sul proprio limite: è una catena di hash lineare, non un albero di Merkle RFC 6962 — sufficiente come prova di manomissione, non per prove di inclusione efficienti. Il sorgente lo dichiara. SiemEvent trasporta l’evento tipizzato con toCanonicalJson(). SiemEventSeverity e SiemEventType lo classificano. CorrelationContext e CorrelationIdGenerator propagano un id di correlazione tra eventi correlati.

RenderReportBuilder, RenderReport, PilotReportAggregator e PilotSummary costituiscono la superficie dei report (@since 5.1.0). L’aggregatore raccoglie i RenderReport e produce un PilotSummary che viene reso come array, JSON o Markdown — il formato consumato da una revisione operativa.

HsmAuditLogInterface / HsmAuditEvent registrano le operazioni di firma assistite da HSM per il livello di sicurezza. MetricsCounterInterface, MetricsGaugeInterface, MetricsHistogramInterface e TraceSpanInterface definiscono le forme di metrics/trace. Le implementazioni NoOp* forniscono un fallback inerte completo, così che il motore possa emettere metriche e span anche senza un backend configurato.

Stabilità: sperimentale. Il log SIEM è internamente contrassegnato per ciclo anziché portare un @since semver congelato, e la superficie di reportistica è @since 5.1.0. Le superfici sono funzionali e testate, ma le forme dell’API possono evolvere. Considerare il formato del log (JSON canonico + catena di hash) come il contratto stabile e l’API PHP come ancora in fase di assestamento.

ClasseMembri principaliRuolo
HashChainSiemEventLogemit(SiemEvent), verifyIntegrity(): ?int, readAll(): GeneratorLog SIEM con catena di hash a prova di manomissione
SiemEventtoCanonicalJson()Evento SIEM tipizzato
SiemEventSeverity / SiemEventType (enum)classificazioneSeverità e tipo dell’evento
CorrelationContext / CorrelationIdGeneratorpropagazione della correlazioneCorrela eventi correlati
RenderReportBuilder / RenderReportassemblaggio del reportReport di un singolo rendering (@since 5.1.0)
PilotReportAggregatoraddReport(), count(), getSummary(), toJson(), toMarkdown(), exportReportsJson()Aggrega i report dei rendering (@since 5.1.0)
PilotSummarytoArray(), toJson(), toMarkdown()Sintesi per revisione operativa (@since 5.1.0)
HsmAuditLogInterface / HsmAuditEventRecord di audit HSMLog di audit delle operazioni HSM
NoOpSiemEventEmitter, NoOpMetricsCounter, NoOpTraceSpan, …fallback inertiImplementazioni no-op complete

Eseguire composer docs:generate-api-php -- --module=Observability per la tabella PHPDoc completa.

Emettere un evento e verificare l’integrità del log.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Observability\Siem\HashChainSiemEventLog;
use NextPDF\Observability\Siem\SiemEvent;
$log = new HashChainSiemEventLog('/var/log/nextpdf/siem.jsonl');
$log->emit(new SiemEvent(/* type, severity, payload */));
$firstBroken = $log->verifyIntegrity();
echo $firstBroken === null
? "SIEM chain intact.\n"
: "Tamper detected at record {$firstBroken}.\n";

Incapsulare l’emitter in modo che un errore di logging nel percorso critico di firma resti una decisione locale, non un’eccezione non gestita.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Observability\Siem\HashChainSiemEventLog;
use NextPDF\Observability\Siem\Exception\SiemEmitterException;
use NextPDF\Observability\Siem\SiemEvent;
use Psr\Log\LoggerInterface;
final readonly class AuditedSiemSink
{
public function __construct(
private HashChainSiemEventLog $log,
private LoggerInterface $fallback,
) {}
public function record(SiemEvent $event): void
{
try {
$this->log->emit($event);
} catch (SiemEmitterException $e) {
// Do not let SIEM I/O abort the signing path; record and continue.
$this->fallback->critical('SIEM emit failed; event not chained.', [
'error' => $e->getMessage(),
]);
}
}
}
  • emit() solleva SiemEmitterException in caso di errore di scrittura. Un chiamante nel percorso critico di firma deve incapsularla e decidere localmente se ignorarla, ritentare o interrompere. L’emitter non decide al posto del chiamante.
  • verifyIntegrity() restituisce l’indice del primo record corrotto, oppure null. Un risultato non-null significa che il log è compromesso da quel punto in poi — non fidarsi dei record a partire da quel record.
  • Il flock advisory è per processo e sullo stesso file. La concorrenza tra host diversi richiede un sink fuori banda (inoltro a syslog). Non assumere che il blocco del file coordini macchine diverse.
  • Questa è una catena di hash lineare, non un albero di Merkle. Fornisce prova di manomissione, non prove di inclusione efficienti. Non commercializzarla come tale.
  • I fallback NoOp* sono completi e inerti. Non diramare in base alla disponibilità del backend per “risparmiare lavoro” — il no-op ha già costo nullo.

emit() legge l’hash del record precedente e accoda una riga sotto un blocco del file — O(1) per evento più il blocco. verifyIntegrity() è O(n) rispetto al numero di record, perché percorre l’intera catena. Eseguirlo in modo pianificato, non nel percorso critico. L’aggregazione dei report è lineare rispetto al numero di report. Il profilo di riproducibilità è structural: eventi e report portano timestamp e id di correlazione, quindi due esecuzioni differiscono in quei campi mentre la struttura della catena resta deterministica.

Il log SIEM è un controllo di sicurezza. La sua prova di manomissione dipende dalla protezione del file di log e dal passaggio di verifica: archiviare il file su storage append-friendly e con accesso controllato, eseguire verifyIntegrity() in modo pianificato e inoltrare i record fuori banda, così che la compromissione di un host non possa riscrivere silenziosamente la cronologia. Gli eventi possono contenere contesto sensibile. Applicare l’obbligo di scrubbing del log del progetto prima che l’evento sia costruito, non dopo che è stato concatenato, perché una riscrittura ripulita spezzerebbe la catena. Il log di audit HSM registra le operazioni di firma ed è a sua volta rilevante per la sicurezza. Trattarlo con le stesse protezioni. Vedere il threat model del motore in /modules/core/security/.

Questo modulo non avanza alcuna affermazione normativa relativa alla specifica PDF. Implementa meccanismi di integrità del log e di osservabilità il cui design è allineato alle pratiche di gestione del log e di verifica dell’integrità del NIST SP 800-92 — un allineamento a un framework di controllo documentato nel sorgente, non una citazione PDF ancorata a un chunk. La conformità dei documenti prodotti dal motore è validata dalle suite oracle e golden descritte in /modules/core/conformance/.